# Beginner question, how to count number of keys in map

I’m trying to get the number keys of a map. the code I came up with is since I have to use map and set and prelude only

``````type Film = (Fan, Title, Review)
filmRating :: [Film] -> [(Fan, Int)]
filmRating l = undefined

Map.fromListWith (++) [(f, [t]) | (f, t, _) <- l]
``````

I put [t] so I could then count that list, but apparently im doing it wrong somewhere and it doesnt work

I want it to give me something like this:

input : [(“fan1”, “film1”, 3), (“fan1”, “film2”, 3), (“fan3”, “film1”, 5), (“fan4”, “film3”, 8)]
output: [(“fan1”, 2). (“fan3”, 1), (“fan4”, 1)] (number of films a fan watched)

is there another way other than recursion? using map / set

Hello. Here is a solution if you are allowed to import `Data.Map` from the `containers` package:

``````import qualified Data.Map.Strict as M

filmRating :: [Film] -> [(Fan, Int)]
filmRating = M.assocs . foldl (\m (fan, _, _) -> M.alter incrCount fan m) M.empty
where
incrCount Nothing = Just 1
incrCount (Just n) = Just (n + 1)
``````
1 Like

is there a way to check for duplicate films watched and not count them?

is there another way other than recursion? using map / set

You were almost there with your suggestion

``````Map.fromListWith (++) [(f, [t]) | (f, t, _) <- l]
``````

However, this has type `Map Fan [Title]`, which is not quite what we
want:

``````>>> example = [("fan1", "film1", 3), ("fan1", "film2", 3), ("fan3", "film1", 5), ("fan4", "film3", 8)]
>>> f films = Map.fromListWith (++) [(f, [t]) | (f, t, _) <- films]
>>> f example
fromList [("fan1",["film2","film1"]),("fan4",["film3"]),("fan3",["film1"])]
``````

However, all hope is not lost! Observe that to get to our solution we
only need to count the length of the list of movies associated to each
fan and replace the list with that number. Sounds like a job for
`Map.map :: Map k v -> Map k v'`, doesn’t it?

``````>>> g :: [Film] -> Map Fan Int
>>> g films = Map.map length (f films)
``````

To fulfill the API of `filmRating`, you now only have to turn that map
back into a list.

Also, if you only want to know the number of movies that a certain
person watched and you don’t want to work with the list at all, you can
combine these two operations quite nicely using your original idea of
leveraging `fromListWith`:

1 Like

You mean not incrementing if someone has already seen the film? You can build a `Map Fan (Set Title)` instead of a `Map Fan Int`, then mapping a function like `Set.size` to get a `Map Fan Int` not counting the duplicates.

1 Like

this makes so much sense to me now. thank you

ah yes thank you idk why my mind didnt go to sets

In general anything of the form `Semigroup v => Map k v` where `v` could be a `Sum Int` is handy for counts of distinct things, like keeping a running histogram and the like. Once things are in this form you can compute the total with a `unionWith mappend` ^^ have fun!

2 Likes

Of course there is. But you need to understand your task components. What it means to be “done” for them.

Or you’ll waste time on playing type tetris with random functions ending with a code soup.
You don’t even need Map for this task unless your data set is huge. And you can swap out your accumulator later, when the algorithm is in place.

1 Like