Poriferous
Poriferous

Reputation: 1582

Filtering for values in 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?

Upvotes: 0

Views: 775

Answers (2)

simpleigh
simpleigh

Reputation: 2894

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?

Upvotes: 5

mariosangiorgio
mariosangiorgio

Reputation: 5543

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.

Upvotes: 0

Related Questions