Home > Software engineering >  How can I count in tuples?
How can I count in tuples?

Time:11-26

I'm trying to return a list of the fans who give the same score for all the movies

so my list is for example: [("fan1", "f1", 3), ("fan1", "f2", 3), ("fan2", "f1", 5)]

and this should return ["fan1", "fan2"] because fan1 rated 2 films the same and also fan2 because its the only film it rated

so far I came up with the following code:

f :: [Film] -> [Fan]
f = nub . map (\(f, _, _) -> f) . filter (\(_, _, s) -> s == 5)

but this only gives me the fans who rated a movie 5 stars and I don't know what to do from here. I am trying use Sets and Maps

CodePudding user response:

First, your type Film has a bad name if the fields are a fan-identifier and rating. A better name would be FilmRating.

But that isn't anyway a good type to solve you task, because you're thinking in terms of what fan has done XYZ. So the structure should actually rather be something like

[(Fan, (FilmName, Grade))]

or even

import qualified Data.Map as M

M.Map Fan (M.Map FilmName Grade)

To get there, you can first use a combination of sortBy and groupBy to gather all the gradings by the same fan into a list each, like

[ [("fan1", "f1", 3), ("fan1", "f2", 3)]
, [("fan2", "f1", 5)]
]

This can be simplified further by mapping a function that removes the redundant information of the fan names:

map (\l@((fanName,_,_):_) -> (fanName, map (\(_, filmName, grade) -> (filmName, grade)) l))

to get

[ ("fan1", [("f1", 3), ("f2", 3)]
, ("fan2", [("f1", 5)]
]

Over this list of lists, you can then use filter to get the fans that fulfill the property you're interested in. That requires a predicate

allRatingsSame :: [(a, Grade)] -> Bool

This can be done by a similar pattern matching,

allRatingsSame l@((_,g0):_) = all (___) l

With that you can then use

filter (\(_, ratings) -> allRatingsSame ratings)

CodePudding user response:

Here's how I'd do it:

import qualified Data.Map as Map
import Data.Maybe

f = Map.keys . Map.filter isJust . Map.fromListWith go . map (\(x,_,y) -> (x,Just y))
  where go (Just x) (Just y) | x == y = Just x
        go _ _ = Nothing

First, we transform the list by throwing away the film name we don't need, and also wrap each rating in Just for a later step. This results in a list like [("fan1", Just 3), ("fan1", Just 3), ("fan2", Just 5)]. Next, we turn this list of pairs into a map, combining duplicates with the go function. This results in a map where the value is Just x where x is the single rating the fan gave to every film, or Nothing if the fan gave different ratings to different films. Finally, we return the keys of all elements whose values are Just something.

  • Related