Convert Seq[Option[Map[String, Any]]] to Option[Map[String, Any]] - scala

How to Convert Seq[Option[Map[String, Any]]] to Option[Map[String, Any]]
So skip any option that is None, and keep the valid Maps and merge them. If every option is None, then the final option should be None as well.

A flatMap along with groupMapReduce, followed by an Option filter should do the job:
val listOfMaps: List[Option[Map[String, Any]]] =
List(Some(Map("a"->"p", "b"->2)), None, Some(Map("a"->"q", "c"->"r")))
val mergedMap = listOfMaps.
flatMap(_.getOrElse(Map.empty[String, Any])).
groupMapReduce(_._1)(t => List[Any](t._2))(_ ::: _)
// mergedMap: Map[String, List[Any]] =
// Map("a" -> List("p", "q"), "b" -> List(2), "c" -> List("r"))
Option(mergedMap).filter(_.nonEmpty)
// res1: Option[Map[String, List[Any]]] =
// Some(Map("a" -> List("p", "q"), "b" -> List(2), "c" -> List("r")))
A few notes:
groupMapReduce is available only on Scala 2.13+.
If you must stick to Seq instead of List, simply replace method ::: with ++ in groupMapReduce.
It is assumed that merging of the Maps means aggregating the Map values of a common key into a List. Replace groupMapReduce with toMap if keeping only one of the Map values of a common key is wanted instead.
This solution treats Some(Map(.empty[String, Any])) the same as None.

This is a one-line solution
val in: Seq[Option[Map[String, Any]]] = ???
val out: Option[Map[String, Any]] =
in.flatten.headOption.map(_ => in.flatten.reduce(_ ++ _))
The in.flatten.headOption is a simple way of getting Some if at least one of the elements is Some or None if they are all None.
The reduce just combines all the Maps into one.
This can clearly be optimised by avoiding the duplicate in.flatten call.

Here is an example with flatten,fold, ++ and a match at the end to provide Some or None.
baz.scala
package baz
object baz {
// fixtures
val x0 = Seq[Option[Map[String, Any]]]()
val x1 = Seq[Option[Map[String, Any]]](None)
val x2 = Seq[Option[Map[String, Any]]](Some(Map("a" -> 1, "b" -> "two")))
val x3 = Seq[Option[Map[String, Any]]](Some(Map("a" -> 1, "b" -> "two")), Some(Map("c" -> 3.0)), None)
def f(x: Seq[Option[Map[String, Any]]]) =
x.flatten.fold(Map[String, Any]())((a,b) => a ++ b) match { case m if m.isEmpty => None case m => Some(m) }
}
Sample run
bash-3.2$ scalac baz.scala && scala -classpath .
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions for evaluation. Or try :help.
scala> import baz.baz._
import baz.baz._
scala> x0 -> f(x0)
res0: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(),None)
scala> x1 -> f(x1)
res1: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(None),None)
scala> x2 -> f(x2)
res2: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(Some(Map(a -> 1, b -> two))),Some(Map(a -> 1, b -> two)))
scala> x3 -> f(x3)
res3: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(Some(Map(a -> 1, b -> two)), Some(Map(c -> 3.0)), None),Some(Map(a -> 1, b -> two, c -> 3.0)))
scala> :quit

Related

Scala - Get the nearest key value from both sides

I'm working on getting the nearest key value in both ways with a given input integer.
Example:
The defined map is as below.
val m = Map(10 -> "W",20 -> "W",30 -> "I",40 -> "A",50 -> "P",60 -> "S",70 -> "A",80 -> "A",90 -> "A",100 -> "I",110 -> "A",120 -> "E")
The keys are integers and they increase in order.
If say, 95 is given as input, then I should get the tuple output as follows
((90->"A"), (100->"I"))
scala> m.map( x=> (x._1,x._2)).toList.sortBy(_._1).filter( _._1<=95 ).last
res74: (Int, String) = (90,A)
scala> m.map( x=> (x._1,x._2)).toList.sortBy(_._1).filter( _._1>=95 ).head
res75: (Int, String) = (100,I)
scala>
The Map size will be big in real scenario(1K) and I want to avoid the sortBy(). Are there any possible foldLeft or map solutions available to this?.
Here's a pretty straight forward solution. No sorting required.
def nearest[V](m :Map[Int,V], k :Int) :Seq[(Int,V)] =
m.get(k).fold {
val (before, after) = m.keys.partition(_ < k)
Seq(before.maxOption, after.minOption).flatten.map(x => (x,m(x)))
}(v => Seq((k,v)))
testing:
val m = Map(10 -> "W",20 -> "W",30 -> "I",40 -> "A",50 -> "P",60 -> "S",70 -> "A",80 -> "A",90 -> "A",100 -> "I",110 -> "A",120 -> "E")
nearest(m, -7) //res0: Seq[(Int, String)] = List((10,W))
nearest(m, 60) //res1: Seq[(Int, String)] = List((60,S))
nearest(m, 93) //res2: Seq[(Int, String)] = List((90,A), (100,I))
nearest(m, 121) //res3: Seq[(Int, String)] = List((120,E))
Here's one approach that leverages TreeMap's sorting and range query features as shown below:
def nearestValues(m: Map[Int, String], key: Int) = {
import scala.collection.immutable.TreeMap
val tm = TreeMap(m.toSeq: _*)
Seq(tm.to(key).lastOption, tm.from(key).headOption).flatten.distinct
}
val m = Map(
10 -> "W", 20 -> "W", 30 -> "I", 40 -> "A", 50 -> "P", 60 -> "S",
70 -> "A", 80 -> "A", 90 -> "A", 100 -> "I", 110 -> "A", 120 -> "E"
)
nearestValues(m, 95)
// res1: Seq[(Int, String)] = List((90,A), (100,I))
nearestValues(m, 20)
// res2: Seq[(Int, String)] = List((20,W))
nearestValues(m, 125)
// res3: Seq[(Int, String)] = List((120,E))
Note that the above method returns a Seq rather than a Tuple to accommodate cases of exact or one-sided matches. To return a Tuple, one could go with something similar to the following:
def nearestValues(m: Map[Int, String], key: Int) = {
import scala.collection.immutable.TreeMap
val tm = TreeMap(m.toSeq: _*)
Seq(tm.to(key).lastOption, tm.from(key).headOption) match {
case Seq(None, None) => (0 -> "", 0 -> "") // Default tuple for empty Map
case Seq(x, None) => (x.get, Int.MaxValue -> "")
case Seq(None, y) => (Int.MinValue -> "", y.get)
case Seq(x, y) => (x.get, y.get)
}
}
nearestValues(m, 95)
// res1: ((Int, String), (Int, String)) = ((90,A),(100,I))
nearestValues(m, 20)
// res2: ((Int, String), (Int, String)) = ((20,W),(20,W))
nearestValues(m, 125)
// res3: ((Int, String), (Int, String)) = ((120,E),(2147483647,""))
UPDATE:
Starting Scala 2.13, methods to and from for TreeMap are replaced with rangeTo and rangeFrom, respectively.
You can use sorted collection as Leo C proposed, however that requires collection construction and then search, so complexity of such algorithm will be O(n*log n).
Approaching this task in algorithmic way, you can calculate the difference for the keys and then find the closest negative and positive values to 0. In this case complexity will be lower O(n).
Below example returns nearest keys from the left and the right excluding exact match (you may change that by changing a condition in the filter):
val data = Map(1-> "a", 5->"b")
val key = 4
val diffs = data.keys.map(_ - key)
val rightKeyOpt = diffs.filter(_ > 0).reduceOption(_ min _).map(_ + key)
val leftKeyOpt = diffs.filter(_ < 0).reduceOption(_ max _).map(_ + key)
val result = (leftKeyOpt.map(k=> k->data(k)), rightKeyOpt.map(k=> k->data(k)))
println (leftKeyOpt, rightKeyOpt)
println result
I bet this could be done in a single line if you need that very much:)

Scala Map Reduction and Aggregation

I have a Map that looks like this and is of Type Map[String, Seq[String]]
Map(
"5" -> Seq("5.1"),
"5.1" -> Seq("5.1.1", "5.1.2"),
"5.1.1" -> Seq("5.1.1.1"),
"5.1.2" -> Seq.empty[String],
"5.1.1.1" -> Seq.empty[String]
)
Given a key, I would like to fetch all the values recursively that belongs to the given key. Say for example., if I want to look up for the key 5, I expect the result to be:
Given Input is: 5
Expected Output is: Seq(5.1, 5.1.1, 5.1.2, 5.1.1.1)
Here is what I tried so far:
def fetchSequence(inputId: String, acc: Seq[String], seqMap: Map[String, Seq[String]]): Seq[String] = seqMap.get(inputId) match {
case None => acc
case Some(subSeq) =>
val newAcc = acc ++ subSeq
subSeq.collect {
case subId=> fetchSequence(subId, newAcc, seqMap)
}.flatten
}
I get an empty result when I call fetchSequence with the Map that I have above.
Somewhat more concise :
def recGet[A](map: Map[A, Seq[A]])(key: A): Seq[A] =
map.get(key).fold(
// return empty Seq if key not found
Seq.empty[A])(
// return a Seq with
// the key and
// the result of recGet called recursively
// (for all the elements in the Seq[A] found for key)
x => Seq(key) ++ x.flatMap(recGet(map)))
You can use recGet as :
val sections = Map(
"5" -> Seq("5.1"),
"5.1" -> Seq("5.1.1", "5.1.2"),
"5.1.1" -> Seq("5.1.1.1"),
"5.1.2" -> Seq.empty[String],
"5.1.1.1" -> Seq.empty[String]
)
recGet(sections)("5") // Seq[String] = List(5, 5.1, 5.1.1, 5.1.1.1, 5.1.2)
recGet(sections)("5.1.1") // Seq[String] = List(5.1.1, 5.1.1.1)
recGet(sections)("5.2") // Seq[String] = List()
This will also give you the (first) element itself (if it exists in the map), if you don't want that, you can probably wrap recGet in another method which uses drop(1) on the result of recGet.

Updating immutable map as the side effect of getOrElse

Sometimes I use a Map as a memoization cache. With mutable maps, I use getOrElseUpdate:
mutableMap.getOrElseUpdate(key, {
val value = <compute the value>
value
})
Immutable maps don't have getOrElseUpdate. So I want to do this
immutableMap.getOrElse(key, {
val value = <compute the value>
immutableMap += key -> value
value
})
This seems to work in practice, I have good arguments to believe it works in theory, and it's more or less readable -- is it a terrible idea for some reason I'm missing?
The other alternatives I'm considering are
immutableMap.get(key) match {
case Some(value) => value
case None =>
val value = <compute the value>
immutableMap += key -> value
value
}
which is not much different and is more cumbersome, or
if (immutableMap.contains(key)) {
immutableMap(key)
} else {
val value = <compute the value>
immutableMap += key -> value
value
}
which is the dumbest and probably least idiomatic.
In principle I rather not go for a solution that uses a helper to return the value and the updated map, unless it's the unarguably superior way.
Sure, it seems reasonable except for one small issue... it's not updating your collection! If you're using an immutable Map, then that Map is immutable. You can not change it, ever.
In fact, immutable Map from Scala collection does not even have a += method defined on it, see immutable.Map. All the methods with "append" or "add" new values to the Map actually return a new Map. So for what you've written above to compile, you'd have to not be using something immutable.
To do this with an immutable map, you'll need to work with a var and replace that var with the new Map (which can lead to issues with threading) or you have to adopt a State Monad type pattern in which you return not only the new value but also the new Map.
def getOrCalc(m: Map[Key, Value], k: Key)(f: Key => Value): (Map[Key, Value], Value] ={
if(m.contains(k)) (m, m(k))
else{
val value = f(k)
(m +: (k, value), value)
}
}
My only recommendation (regarding the reasons why you choosed var instead of mutable.Map or Java's ConcurrentMap) is to wrap it into DSL, like:
case class Mutable[K,V](var m: Map[K,V]) {
def orElseUpdate(key: K, compute: => V) = m.getOrElse(key, {
val value = compute
m += key -> value
value
})
}
scala> val a = Mutable(Map(1 -> 2))
a: Mutable[Int,Int] = Mutable(Map(1 -> 2))
scala> a.orElseUpdate(2, 4)
res10: Int = 4
scala> a.orElseUpdate(2, 6)
res11: Int = 4
scala> a.orElseUpdate(3, 6)
res12: Int = 6
Another option (if your computation is lightweight) is just:
m += key -> m.getOrElse(key, compute)
m(key)
Example:
scala> var m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res3: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 6)
scala> m
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
You can wrap it into DSL as well:
implicit class RichMap[K,V](m: Map[K,V]) {
def kvOrElse(k: K, v: V) = k -> m.getOrElse(k, v)
}
scala> m += m.kvOrElse(3, 7)
scala> m
res7: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += m.kvOrElse(4, 7)
scala> m
res9: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5, 4 -> 7)

What is the syntax for adding an element to a scala.collection.mutable.Map?

What is the syntax for adding an element to a scala.collection.mutable.Map ?
Here are some failed attempts:
val map = scala.collection.mutable.Map
map("mykey") = "myval"
map += "mykey" -> "myval"
map.put("mykey","myval")
The point is that the first line of your code is not what you expected.
You should use:
val map = scala.collection.mutable.Map[A,B]()
You then have multiple equivalent alternatives to add items:
scala> val map = scala.collection.mutable.Map[String,String]()
map: scala.collection.mutable.Map[String,String] = Map()
scala> map("k1") = "v1"
scala> map
res1: scala.collection.mutable.Map[String,String] = Map((k1,v1))
scala> map += "k2" -> "v2"
res2: map.type = Map((k1,v1), (k2,v2))
scala> map.put("k3", "v3")
res3: Option[String] = None
scala> map
res4: scala.collection.mutable.Map[String,String] = Map((k3,v3), (k1,v1), (k2,v2))
And starting Scala 2.13:
scala> map.addOne("k4" -> "v4")
res5: map.type = HashMap(k1 -> v1, k2 -> v2, k3 -> v3, k4 -> v4)
As always, you should question whether you truly need a mutable map.
Immutable maps are trivial to build:
val map = Map(
"mykey" -> "myval",
"myotherkey" -> "otherval"
)
Mutable maps are no different when first being built:
val map = collection.mutable.Map(
"mykey" -> "myval",
"myotherkey" -> "otherval"
)
map += "nextkey" -> "nextval"
In both of these cases, inference will be used to determine the correct type parameters for the Map instance.
You can also hold an immutable map in a var, the variable will then be updated with a new immutable map instance every time you perform an "update"
var map = Map(
"mykey" -> "myval",
"myotherkey" -> "otherval"
)
map += "nextkey" -> "nextval"
If you don't have any initial values, you can use Map.empty:
val map : Map[String, String] = Map.empty //immutable
val map = Map.empty[String,String] //immutable
val map = collection.mutable.Map.empty[String,String] //mutable
When you say
val map = scala.collection.mutable.Map
you are not creating a map instance, but instead aliasing the Map type.
map: collection.mutable.Map.type = scala.collection.mutable.Map$#fae93e
Try instead the following:
scala> val map = scala.collection.mutable.Map[String, Int]()
map: scala.collection.mutable.Map[String,Int] = Map()
scala> map("asdf") = 9
scala> map
res6: scala.collection.mutable.Map[String,Int] = Map((asdf,9))
Create a new immutable map:
scala> val m1 = Map("k0" -> "v0")
m1: scala.collection.immutable.Map[String,String] = Map(k0 -> v0)
Add a new key/value pair to the above map (and create a new map, since they're both immutable):
scala> val m2 = m1 + ("k1" -> "v1")
m2: scala.collection.immutable.Map[String,String] = Map(k0 -> v0, k1 -> v1)
var test = scala.collection.mutable.Map.empty[String, String]
test("myKey") = "myValue"
Create a mutable map without initial value:
scala> var d= collection.mutable.Map[Any, Any]()
d: scala.collection.mutable.Map[Any,Any] = Map()
Create a mutable map with initial values:
scala> var d= collection.mutable.Map[Any, Any]("a"->3,1->234,2->"test")
d: scala.collection.mutable.Map[Any,Any] = Map(2 -> test, a -> 3, 1 -> 234)
Update existing key-value:
scala> d("a")= "ABC"
Add new key-value:
scala> d(100)= "new element"
Check the updated map:
scala> d
res123: scala.collection.mutable.Map[Any,Any] = Map(2 -> test, 100 -> new element, a -> ABC, 1 -> 234)
var map:Map[String, String] = Map()
var map1 = map + ("red" -> "#FF0000")
println(map1)

Scala: how to merge a collection of Maps

I have a List of Map[String, Double], and I'd like to merge their contents into a single Map[String, Double]. How should I do this in an idiomatic way? I imagine that I should be able to do this with a fold. Something like:
val newMap = Map[String, Double]() /: listOfMaps { (accumulator, m) => ... }
Furthermore, I'd like to handle key collisions in a generic way. That is, if I add a key to the map that already exists, I should be able to specify a function that returns a Double (in this case) and takes the existing value for that key, plus the value I'm trying to add. If the key does not yet exist in the map, then just add it and its value unaltered.
In my specific case I'd like to build a single Map[String, Double] such that if the map already contains a key, then the Double will be added to the existing map value.
I'm working with mutable maps in my specific code, but I'm interested in more generic solutions, if possible.
Well, you could do:
mapList reduce (_ ++ _)
except for the special requirement for collision.
Since you do have that special requirement, perhaps the best would be doing something like this (2.8):
def combine(m1: Map, m2: Map): Map = {
val k1 = Set(m1.keysIterator.toList: _*)
val k2 = Set(m2.keysIterator.toList: _*)
val intersection = k1 & k2
val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key)))
val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_))
r2 ++ r1
}
You can then add this method to the map class through the Pimp My Library pattern, and use it in the original example instead of "++":
class CombiningMap(m1: Map[Symbol, Double]) {
def combine(m2: Map[Symbol, Double]) = {
val k1 = Set(m1.keysIterator.toList: _*)
val k2 = Set(m2.keysIterator.toList: _*)
val intersection = k1 & k2
val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key)))
val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_))
r2 ++ r1
}
}
// Then use this:
implicit def toCombining(m: Map[Symbol, Double]) = new CombiningMap(m)
// And finish with:
mapList reduce (_ combine _)
While this was written in 2.8, so keysIterator becomes keys for 2.7, filterKeys might need to be written in terms of filter and map, & becomes **, and so on, it shouldn't be too different.
How about this one:
def mergeMap[A, B](ms: List[Map[A, B]])(f: (B, B) => B): Map[A, B] =
(Map[A, B]() /: (for (m <- ms; kv <- m) yield kv)) { (a, kv) =>
a + (if (a.contains(kv._1)) kv._1 -> f(a(kv._1), kv._2) else kv)
}
val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
val mm = mergeMap(ms)((v1, v2) => v1 + v2)
println(mm) // prints Map(hello -> 5.5, world -> 2.2, goodbye -> 3.3)
And it works in both 2.7.5 and 2.8.0.
I'm surprised no one's come up with this solution yet:
myListOfMaps.flatten.toMap
Does exactly what you need:
Merges the list to a single Map
Weeds out any duplicate keys
Example:
scala> List(Map('a -> 1), Map('b -> 2), Map('c -> 3), Map('a -> 4, 'b -> 5)).flatten.toMap
res7: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 4, 'b -> 5, 'c -> 3)
flatten turns the list of maps into a flat list of tuples, toMap turns the list of tuples into a map with all the duplicate keys removed
Starting Scala 2.13, another solution which handles duplicate keys and is only based on the standard library consists in merging the Maps as sequences (flatten) before applying the new groupMapReduce operator which (as its name suggests) is an equivalent of a groupBy followed by a mapping and a reduce step of grouped values:
List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
.flatten
.groupMapReduce(_._1)(_._2)(_ + _)
// Map("world" -> 2.2, "goodbye" -> 3.3, "hello" -> 5.5)
This:
flattens (concatenates) the maps as a sequence of tuples (List(("hello", 1.1), ("world", 2.2), ("goodbye", 3.3), ("hello", 4.4))), which keeps all key/values (even duplicate keys)
groups elements based on their first tuple part (_._1) (group part of groupMapReduce)
maps grouped values to their second tuple part (_._2) (map part of groupMapReduce)
reduces mapped grouped values (_+_) by taking their sum (but it can be any reduce: (T, T) => T function) (reduce part of groupMapReduce)
The groupMapReduce step can be seen as a one-pass version equivalent of:
list.groupBy(_._1).mapValues(_.map(_._2).reduce(_ + _))
Interesting, noodling around with this a bit, I got the following (on 2.7.5):
General Maps:
def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: Seq[scala.collection.Map[A,B]]): Map[A, B] = {
listOfMaps.foldLeft(Map[A, B]()) { (m, s) =>
Map(
s.projection.map { pair =>
if (m contains pair._1)
(pair._1, collisionFunc(m(pair._1), pair._2))
else
pair
}.force.toList:_*)
}
}
But man, that is hideous with the projection and forcing and toList and whatnot. Separate question: what's a better way to deal with that within the fold?
For mutable Maps, which is what I was dealing with in my code, and with a less general solution, I got this:
def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: List[mutable.Map[A,B]]): mutable.Map[A, B] = {
listOfMaps.foldLeft(mutable.Map[A,B]()) {
(m, s) =>
for (k <- s.keys) {
if (m contains k)
m(k) = collisionFunc(m(k), s(k))
else
m(k) = s(k)
}
m
}
}
That seems a little bit cleaner, but will only work with mutable Maps as it's written. Interestingly, I first tried the above (before I asked the question) using /: instead of foldLeft, but I was getting type errors. I thought /: and foldLeft were basically equivalent, but the compiler kept complaining that I needed explicit types for (m, s). What's up with that?
I reading this question quickly so I'm not sure if I'm missing something (like it has to work for 2.7.x or no scalaz):
import scalaz._
import Scalaz._
val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
ms.reduceLeft(_ |+| _)
// returns Map(goodbye -> 3.3, hello -> 5.5, world -> 2.2)
You can change the monoid definition for Double and get another way to accumulate the values, here getting the max:
implicit val dbsg: Semigroup[Double] = semigroup((a,b) => math.max(a,b))
ms.reduceLeft(_ |+| _)
// returns Map(goodbye -> 3.3, hello -> 4.4, world -> 2.2)
I wrote a blog post about this , check it out :
http://www.nimrodstech.com/scala-map-merge/
basically using scalaz semi group you can achieve this pretty easily
would look something like :
import scalaz.Scalaz._
listOfMaps reduce(_ |+| _)
a oneliner helper-func, whose usage reads almost as clean as using scalaz:
def mergeMaps[K,V](m1: Map[K,V], m2: Map[K,V])(f: (V,V) => V): Map[K,V] =
(m1 -- m2.keySet) ++ (m2 -- m1.keySet) ++ (for (k <- m1.keySet & m2.keySet) yield { k -> f(m1(k), m2(k)) })
val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
ms.reduceLeft(mergeMaps(_,_)(_ + _))
// returns Map(goodbye -> 3.3, hello -> 5.5, world -> 2.2)
for ultimate readability wrap it in an implicit custom type:
class MyMap[K,V](m1: Map[K,V]) {
def merge(m2: Map[K,V])(f: (V,V) => V) =
(m1 -- m2.keySet) ++ (m2 -- m1.keySet) ++ (for (k <- m1.keySet & m2.keySet) yield { k -> f(m1(k), m2(k)) })
}
implicit def toMyMap[K,V](m: Map[K,V]) = new MyMap(m)
val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
ms reduceLeft { _.merge(_)(_ + _) }