Filtering for values in Haskell - haskell

I've been doing some Haskell exercises from a Haskell book, and one of the tasks is for me to filter for values of a certain type and return them as a list.
import Data.Time
data Item = DbString String
| DbNumber Integer
| DbDate UTCTime
deriving (Eq, Ord, Show)
database :: [Item]
database =
[
DbDate (UTCTime (fromGregorian 1911 5 1) (secondsToDiffTime 34123)),
DbNumber 9001,
DbString "Hello World!",
DbDate (UTCTime (fromGregorian 1921 5 1) (secondsToDiffTime 34123))
]
That's the code I am given to work with, and for my first task:
Write a function that filters for DbDate values and returns a list of the UTCTime values inside them. The template for the function is:
filterDate :: [Item] -> [UTCTime]
filterDate = undefined
What I have to use here are folds since that is the matter concerned here.
I looked up the Data.Time module on Hoogle and that didn't really help since I couldn't understand how to interact with the module. Maybe I'm looking at this from a wrong perspective because I don't think it has something to do with the filter function, and I don't think it has something to do with type-casting neither ::.
How do I get UTCTime values, and how do I filter for them?

OK, my Haskell-fu is extremely weak but I'm going to have a stab at an answer. You're looking to define a function that walks across a list and filters it. If the value is a DbDate then you return <that value> : <output list>, otherwise you return <output list>. By folding over the input you produce a filtered output. There's a relevant question at How would you define map and filter using foldr in Haskell? which might explain this better.
This breaks down to something like:
filterFn :: Item -> [UTCTime] -> [UTCTime]
filterFn (DbDate x) xs = x:xs
filterFn _ xs = xs
(this might be a syntax fail). This function takes an item off our [Item] and pattern matches.
If it matches DbDate x then x is a UTCTime and we append it to our input list.
If it doesn't then we ignore it and return the input list unchanged.
We can then fold:
filterDate = foldr filterFn []
Does that get you to an answer?

Item is defined as a union type, which means it can be a DbString, a DbNumber or a DbDate.
data Item = DbString String
| DbNumber Integer
| DbDate UTCTime
deriving (Eq, Ord, Show)
You can use pattern matching to get only the value you're interested in. You need to match on an item, check whether it is a DbDate and if that's the case extract the UTCTime instance it holds.
You said you want to use a fold so you need an accumulator where you can put the values you want to keep and a function to populate it.
filterDate items = foldl accumulate [] items
where extractTime item = case item of DbDate time -> [time]
_ -> []
accumulate item accumulator = accumulator ++ (extractTime item)
In the code above you have extractTime that pattern matches over an item and either returns a list containing the time or it returns an empty list. The accumulate function just puts together the values you got from the previous steps (they're stored in accumulator) and the value you got applying extractTime to the current item.

Related

How to parse a JSON string using Aeson that can be one of two different types

I'm currently struggling to parse some JSON data using the aeson library. There are a number of properties that have the value false when the data for that property is absent. So if the property's value is typically an array of integers and there happens to be no data for that property, instead of providing an empty array or null, the value is false. (The way that this data is structured isn't my doing so I'll have to work with it somehow.)
Ideally, I would like to end up with an empty list in cases where the value is a boolean. I've created a small test case below for demonstration. Because my Group data constructor expects a list, it fails to parse when it encounters false.
data Group = Group [Int] deriving (Eq, Show)
jsonData1 :: ByteString
jsonData1 = [r|
{
"group" : [1, 2, 4]
}
|]
jsonData2 :: ByteString
jsonData2 = [r|
{
"group" : false
}
|]
instance FromJSON Group where
parseJSON = withObject "group" $ \g -> do
items <- g .:? "group" .!= []
return $ Group items
test1 :: Either String Group
test1 = eitherDecode jsonData1
-- returns "Right (Group [1,2,4])"
test2 :: Either String Group
test2 = eitherDecode jsonData2
-- returns "Left \"Error in $.group: expected [a], encountered Boolean\""
I was initially hoping that the (.!=) operator would allow it to default to an empty list but that only works if the property is absent altogether or null. If it were "group": null, it would parse successfully and I would get Right (Group []).
Any advice for how to get it to successfully parse and return an empty list in these cases where it's false?
One way to solve this problem is to pattern match on the JSON data constructors that are valid for your dataset and raise invalid for all others.
For instance, you could write something like this for that particular field, keeping in mind that parseJSON is a function from Value -> Parser a:
instance FromJSON Group where
parseJSON (Bool False) = Group <$> pure []
parseJSON (Array arr) = pure (Group $ parseListOfInt arr)
parseJSON invalid = typeMismatch "Group" invalid
parseListOfInt :: Vector Value -> [Int]
parseListOfInt = undefined -- build this function
You can see an example of this in the Aeson docs, which are pretty good (but you kind of have to read them closely and a few times through).
I would probably then define a separate record to represent the top-level object that this key comes in and rely on generic deriving, but others may have a better suggestion there:
data GroupObj = GroupObj { group :: Group } deriving (Eq, Show)
instance FromJSON GroupObj
One thing to always keep in mind when working with Aeson are the core constructors (of which there are only 6) and the underlying data structures (HashMap for Object and Vector for Array, for instance).
For example, in the above, when you pattern match on Array arr, you have to be aware that you're getting a Vector Value there in arr and we still have some work to do to turn this into a list of integers, which is why I left that other function parseListOfInt undefined up above because I think it's probably a good exercise to build it?

Haskell accesing the sublist while filtering a list of a list

I'm fairly new to Haskell, and try to accomplish the following:
filter (((!!) subList (fromJust (elemIndex String [String]))) == String) [[string]]
I replaced the names with their types to make it easier to read. The problem is that i can't do subList, but the (!!) takes a list as argument. So how would i go about writing this?
edit:
I wrote the filtering condition seperately, which works, but the problem is still the same:
findGood :: [String] -> String -> [String] -> String -> Bool
but i still need to fill in all the values, though the first [String] is the sublist. All the other values can be entered..
Edit:
I solved it, what i needed to do was put the [String] last, and then i could leave it out in the filter
So
findGood :: String -> [String] -> String -> [String] -> Bool
filter (findGood a b c) [[String]]
Thats what works, and thats what I wanted. (not needing the D)
Given that you're wanting to compare money and check money > 500 it makes much more sense to store it as an Int not a String. Furthermore I would say storing each entry as a tuple and having a list of tuples makes more sense than using a list of lists (this is also required for the Int implementation of money as you can't have a list of different types of lists).
So the table would have type [(String, Int)].
filter goes through the list and checks each element against a condition. In this case the condition is so simple I wouldn't bother making it it's own function (unless you need to reuse it in lots of places).
When given a pair of type (String, Int) we just want to check if that second value is > 500. So the condition is simply snd row > 500 where row is the tuple we were given representing a row in the table.
This leaves us with:
filter (\row -> snd row > 500) table
Testing on [("Geoff", 600), ("Bill", 700), ("Bill Jr.", 10)] this gives [("Geoff", 600), ("Bill", 700)] as expected.

converting a list of string into a list of tuples in Haskell

I have a list of strings:
[" ix = index"," ctr = counter"," tbl = table"]
and I want to create a tuple from it like:
[("ix","index"),("ctr","counter"),("tbl","table")]
I even tried:
genTuple [] = []
genTuples (a:as)= do
i<-splitOn '=' a
genTuples as
return i
Any help would be appriciated
Thank you.
Haskell's type system is really expressive, so I suggest to think about the problem in terms of types. The advantage of this is that you can solve the problem 'top-down' and the whole program can be typechecked as you go, so you can catch all kinds of errors early on. The general approach is to incrementally divide the problem into smaller functions, each of which remaining undefined initially but with some plausible type.
What you want is a function (let's call it convert) which take a list of strings and generates a list of tuples, i.e.
convert :: [String] -> [(String, String)]
convert = undefined
It's clear that each string in the input list will need to be parsed into a 2-tuple of strings. However, it's possible that the parsing can fail - the sheer type String makes no guarantees that your input string is well formed. So your parse function maybe returns a tuple. We get:
parse :: String -> Maybe (String, String)
parse = undefined
We can immediately plug this into our convert function using mapMaybe:
convert :: [String] -> [(String, String)]
convert list = mapMaybe parse list
So far, so good - but parse is literally still undefined. Let's say that it should first verify that the input string is 'valid', and if it is - it splits it. So we'll need
valid :: String -> Bool
valid = undefined
split :: String -> (String, String)
split = undefined
Now we can define parse:
parse :: String -> Maybe (String, String)
parse s | valid s = Just (split s)
| otherwise = Nothing
What makes a string valid? Let's say it has to contain a = sign:
valid :: String -> Bool
valid s = '=' `elem` s
For splitting, we'll take all the characters up to the first = for the first tuple element, and the rest for the second. However, you probably want to trim leading/trailing whitespace as well, so we'll need another function. For now, let's make it a no-op
trim :: String -> String
trim = id
Using this, we can finally define
split :: String -> (String, String)
split s = (trim a, trim (tail b))
where
(a, b) = span (/= '=') s
Note that we can safely call tail here because we know that b is never empty because there's always a separator (that's what valid verified). Type-wise, it would've been nice to express this guarantee using a "non-empty string" but that may be a bit overengineered. :-)
Now, there are a lot of solutions to the problem, this is just one example (and there are ways to shorten the code using eta reduction or existing libraries). The main point I'm trying to get across is that Haskell's type system allows you to approach the problem in a way which is directed by types, which means the compiler helps you fleshing out a solution from the very beginning.
You can do it like this:
import Control.Monda
import Data.List
import Data.List.Split
map ((\[a,b] -> (a,b)) . splitOn "=" . filter (/=' ')) [" ix = index"," ctr = counter"," tbl = table"]

Trouble with the State Monad

I am trying to write a program to generate 'word chains', e.g. bat -> cat -> cot -> bot, using the list monad (mostly comprehensions) to generate combinations of words, and the state monad to build up the actual chain as i go through the possibilities. That second part is giving me trouble:
import Control.Monad.State
type Word = String
type Chain = [Word]
getNext :: Word -> State Chain Word
getNext word = do
list <- get
return (list ++ "current word")
The part where I generate words works and is given below, but as you can see I don't really know what I'm doing in this part. Basically wordVariations :: Word -> [Word] takes a Word and returns a list of Words that differ in one letter from the given word. I'm trying to change this so that each word has a state signifying its predecessors:
For example: input = "cat". the final value is "got", the final state is ["cat","cot","got"]
What I have now will give me "got" from "cat" after 3 steps, but won't tell me how it got there.
None of the State Monad tutorials I found online were terribly helpful. The above code, when compiled with GHC, gives the error:
WordChain.hs:42:11:
Couldn't match type `Word' with `Char'
When using functional dependencies to combine
MonadState s (StateT s m),
arising from the dependency `m -> s'
in the instance declaration in `Control.Monad.State.Class'
MonadState [Char] (StateT Chain Data.Functor.Identity.Identity),
arising from a use of `get' at WordChain.hs:42:11-13
In a stmt of a 'do' block: list <- get
In the expression:
do { list <- get;
return (list ++ "current word") }
Failed, modules loaded: none.
This is just meant to be a test to work off of, but I can't figure it out!
The code in full is below in case it is helpful. I know this may not be the smartest way to do this, but it is a good opportunity to learn about the state monad. I am open to necessary changes in the way the code works also, because I suspect that some major refactoring will be called for:
import Control.Monad.State
type Word = String
type Dict = [String]
-- data Chain = Chain [Word] deriving (Show)
replaceAtIndex :: Int -> a -> [a] -> [a]
replaceAtIndex n item ls = a ++ (item:b) where (a, (_:b)) = splitAt n ls
tryLetter str c = [replaceAtIndex n c str | n <- [0..(length str - 1)]]
wordVariations str = tryLetter str =<< ['a' .. 'z']
testDict :: Dict
testDict = ["cat","cog","cot","dog"]
------- implement state to record chain
type Chain = [Word] -- [cat,cot,got,tot], etc. state var.
startingState = [""] :: Chain
getNext :: Word -> State Chain Word
getNext w = do
list <- get
return ( list ++ "current word")
First of all, the error you posted is in this line return ( list ++ "current word").
"current word" type is Word, which is an alias for String, which is an alias for[Char]`.
The variable list has a type of Chain, which is an alias for [Word], which is an alias for [[Char]].
The type signature of the function forces the return type must be a Word.
++ requires that the types on both sides be a list with the same type, (++) :: [a] -> [a] -> [a].
However, if you plug in the above type signatures, you get the type [Word] -> [Char] -> [Char] which has mismatched "a"s.
Quick but fairly important performance side note: prepending to a list is much faster then appending, so you might want to consider building them backwards and using (:) and reversing them at the end.
The State Monad is not really the right choice for storing the steps used to get to the result. At least it is certainly overkill, when the List Monad would be sufficient to complete the task. Consider:
-- given a list of words, find all possible subsequent lists of words
getNext :: [String] -> [[String]]
getNext words#(newest:_) = fmap (:words) (wordVariations newest)
-- lazily construct all chains of every length for every word
wordChains :: String -> [[[String]]]
wordChains word = chain
where chain = [[word]] : map (>>= getNext) chain
-- all the 5 word long chains starting with the word "bat"
batchains = wordChains "bat" !! 4
(Disclaimer: code compiled, but not run).
getNext takes a Chain, and returns a list containing a list of Chains, where each one has a different prepended successor in the chain. Since they share a common tail, this is memory efficient.
In wordChains, by repeatedly mapping using the list monad with (>>= getNext), you end up with a lazy infinite list, where the zeroth item is a Chain the starting word, the first item is all 2 item Chains where the first is the starting word, the second item is all 3 item chains, and so on. If you just want one chain of length 5, you can grab the head of the 4th item, and it will do just enough computation to create that for you, but you can also get all of them.
Also, getNext could be expanded to not repeat words by filtering.
Finally, when it comes to finding wordVariations, another algorithm would be to construct a filter which returns True if the lengths of two words are the same and the number of characters that are different between them is exactly 1. Then you could filter over a dictionary, instead of trying every possible letter variation.
Good luck!

Adding to list and incrementing - Haskell

import Data.Char
-- Sample test data
testData :: [Movies]
testData = [("Me and My Broken Heart","Rixton"),
("It’s My Birthday","will.i.am"),
("Problem","Ariana Grande")]
-- record a sale of a track
record :: [Movies] -> String -> String
record t a = []
record ((t, a): xs) a a
| t == a && a == a = [(t,a)]
| otherwise = record xs a t
The correct output should be a modified version of the database.
First, since it seems you're learning, a few notes on style: in a case like this, it is a convention to name one sale with a singular form, and the list of them with the plural, that is:
type Sale = (String, String, Int)
type Sales = [Sale]
Even better, one would often (depending on intended usage and taste) turn Sale into a newtype or full ADT, since that gives you more abstraction and type safety.
Second, to your actual question: the behavior you're seeing comes from the order of pattern matching. In your first match,
recordSale testData aTitle anArtist = []
testDate matches any list, also non-empty ones, before the second pattern can apply. Change that to
recordSale [] _ _ = []
and you won't get only empty lists anymore. Additionally, as #Aleksandar notes, you should not forget to keep the init of the list in cases where the list is not empty, but the filter criteria don't match.
Haskell is a functional language, and encapsulates stateful modifications to data very well in the type system. The usual way of "mutating" a value in Haskell would be to either use a stateful computation (in the ST or State Monad) or to just use recursion and duplicate the values. I think you should learn the latter first.
The function you used will return [] in case aTitle and anArtist don't match any of the entries in the list. Otherwise it will return a list with one single element in it, namely the newly modified one.
You need to build up the list incrementally as you traverse it. Remember, you're basically copying it:
-- record a sale of a track
recordSale :: [Sales] -> String -> String -> [Sales]
recordSale [] aTitle anArtist = []
recordSale ((title, artist, qty): xs) aTitle anArtist
| title == aTitle && artist == anArtist =
(title, artist, qty+1):recordSale xs aTitle anArtist
| otherwise = (title,artist,qty):recordSale xs aTitle anArtist
This should give you the right results. Notice what's different here: you have a recursive call to the function in both cases, and in both cases, too, you're appending the current element either modified or unmodified to the list you're building.
You should also think about your data structure. Having tuples with comments is not a very nice way of constructing complex data structures. Try
data Record = Record { title :: String, artist :: String, quantity :: Integer }
You could even make a couple of newtypes like newtype Title = Title String. Remember, creating data types in Haskell is cheap, and you should be doing it constantly.

Resources