using Data.Map.map to sum values

Solution 1:

Gradually, I got to this oneliner:

sumTheDoubles' f = Mapping.map (\a -> map ((\b -> \(c,d) -> (c,b)) ((sum.map snd) a)) a) f

How did I get this result? I started at the highest level. Then I defined more and more functions as needed. Always I first determined the type of individual function. Finally, I converted the functions to lambda form (from the bottom) and applied them to the top level.

module Main where

import qualified Data.Map as Mapping

sumTheDoubles :: Mapping.Map Char [(Char, Double)] -> Mapping.Map Char [(Char, Double)]
sumTheDoubles a = Mapping.map map2 a

-- combine sum and replace
map2 :: [(Char, Double)] -> [(Char, Double)]
map2 a = map (map3 $ map4 a) a

-- replace snd
map3 :: Double -> (Char, Double) -> (Char, Double)
map3 a (b,c) = (b,a)

-- sum Doubles
map4 :: [(Char, Double)] -> Double
map4 a = (sum.map snd) a

main = putStrLn $ show $ sumTheDoubles $ Mapping.fromList [('i', [('a', 1.0), ('n', 2.0)])]

Output:

fromList [('i',[('a',3.0),('n',3.0)])]

Solution 2:

It does not seem to make much sense to construct a Map Char [(Char, Double)]: the second item of all the 2-tuples in the list will be the same. It makes more sense to construct a Map Char Double, where we thus have summed up the second item of the 2-tuples of the list of values.

We can construct such map with:

sumTheDoubles :: Mapping.Map Char [(Char, Double)] -> Mapping.Map Char Double
sumTheDoubles = fmap (sum . map snd)

we here make use of fmap :: Functor f => (a -> b) -> f a -> f b to apply a function to all values in the Map, and for each of these values we first construct a list of second items of the original list, and then use sum to sum these together.

If you really want to use a list of elements, you can work with:

sumTheDoubles :: Mapping.Map Char [(Char, Double)] -> Mapping.Map Char Double
sumTheDoubles = fmap (\xs -> map (sum (map snd xs) <$) xs)