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.