Denis Broghan
Denis Broghan

Reputation: 3

Scala concatenate maps from a list

Having val mapList: List[Map[String, Int]], I want to do something like:

val map = mapList foldLeft (Map[String, Int]()) ( _ ++ _ )

or

val map = mapList foldLeft (Map[String, Int]())
 ( (m1: Map[String, Int], m2: Map[String, Int]) => m1 ++ m2 )

Neither option is compiled (first says "missing parameter type for expanded function (x, y) => x ++ y" and second says "type mismatch; found (Map[String, Int], Map[String, Int]) => Map[String, Int]; required: String").

I want to achieve a classical solution for concatenating a list of immutable maps such as List( Map("apple" -> 5, "pear" -> 7), Map("pear" -> 3, "apricot" -> 0) ) would produce a Map("apple" -> 5, "pear" -> 10, "apricot" -> 0).

Using scala 2.10.5.

Upvotes: 0

Views: 256

Answers (1)

Ben Reich
Ben Reich

Reputation: 16324

You need to add a dot before foldLeft. You can only use spaces instead of dots under specialized conditions, such as for methods with exactly 1 parameter (arity-1 methods):

val map = mapList.foldLeft(Map[String, Int]()) ( _ ++ _ )

You can read more about method invocation best practices here.

You might also be interested in the reduce methods, which are specialized versions of the fold methods, where the return type is the same as the type of the elements of the collection. For example reduceLeft uses the first element of the collection as a seed for the foldLeft. Of course, since this relies on the first element's existence, it will throw an exception if the collection is empty. Since reduceLeft takes only 1 parameter, you can more easily use a space to invoke the method:

mapList.reduceLeft( _ ++ _)
mapList reduceLeft(_ ++ _)

Finally, you should note that all you are doing here is merging the maps. When using ++ to merge the maps, you will just override keys that are already present in the map – you won't be adding the values of duplicate keys. If you wanted to do that, you could follow the answers provided here, and apply them to the foldLeft or reduceLeft. For example:

mapList reduceLeft { (acc, next) => 
    (acc.toList ++ next.toList).groupBy(_._1).toMap.mapValues(_.map(_._2).sum) 
}

Or slightly differently:

mapList.map(_.toSeq).reduceLeft(_ ++ _).groupBy(_._1).toMap.mapValues(_.map(_._2).sum)

And, if you're using Scalaz, then most concisely:

mapList reduceLeft { _ |+| _ }

Upvotes: 3

Related Questions