PablodeAcero
PablodeAcero

Reputation: 411

Haskell select elements in list

I've a problem with a function like this:

data City = City {Car :: String, Weight :: Int, Color :: String}

-I've a list of "City" and my function has to make a list of tuples, and each tuple is (Car, "sum of the weight"), therefore, if a the Car is equal, the weight has to be added, making something like this:

main> [(Porche,180),(Ferrari,400),(Opel,340)]

The car's can't be repeated on the output list, because their wheights must be added.

I was thinking of doing something like making a list with all the car types, and then filtering the weights and add them, making a list, but I just can't make it work.

Upvotes: 0

Views: 3205

Answers (2)

Redu
Redu

Reputation: 26161

In terms of performance, perhaps it's best to use the Data.HashMap.Lazy package for this job. Accordingly you may do it as follows;

import qualified Data.HashMap.Lazy as M

data City = City {car :: String, weight :: Int, color :: String}

ccw :: [City] -> [(String, Int)]
ccw []     = []
ccw (x:xs) = M.toList $ foldr addWeight (M.singleton (car x) (weight x)) xs
             where
             addWeight :: City -> M.HashMap String Int ->  M.HashMap String Int
             addWeight c r = case M.lookup (car c) r of
                             Nothing -> M.insert (car c) (weight c) r
                             Just x  -> M.adjust (+ weight c) (car c) r

λ> ccw [City "Porsche" 180 "Black", City "Ferrari" 400 "Red", City "Opel" 340 "White", City "Porsche" 210 "Yellow"]
[("Opel",340),("Ferrari",400),("Porsche",390)]

Upvotes: 0

Satvik
Satvik

Reputation: 11208

I will guide you to the solution. It is better to understand how to arrive at the solution than the solution itself.

import Data.List
data City = City {car :: String, weight :: Int, color :: String} deriving (Show)

If color has nothing to do with City being equal you can convert the City to a tuple. You can use map to do that.

city2tup :: [City] -> [(String,Int)]
city2tup = map (\(City c w _) -> (c,w))

Now look at function sort and groupBy from Data.List. Sorting and then grouping on the first element will collect together similar cars in a list. So you will have a list of list. Now you just need to fold on each sublist and add corresponding weights.

collect :: [City] -> [(String,Int)]
collect = map (foldl1 collectWeight) . groupBy ((==) `on` fst) . sort . city2tup

You still need to define what collectWeight is but that should be easy.

Upvotes: 4

Related Questions