Combining Maps of Iterables in Scala Without Replacing on Key Conflict

What’s a way of combining Maps of Iterables without losing information? In Scala, it’s common to combine or concatenate Iterables, usually something like this:

val a = Vector(1, 2, 3)
val b = Vector(4, 5, 6)
val c = a ++ b

This takes Vector a and concatenates it with Vector b. The resulting Vector c will end up being:

Vector(1, 2, 3, 4, 5, 6)

This works fine for most Iterables, though for Maps the behavior might be a little unexpected. When concatenating Maps, if there is a collision of keys, then the value from the second Map will be used.

val a = Map(1 -> "a", 2 -> "b", 3 -> "c")
val b = Map(4 -> "d", 2 -> "e", 5 -> "f")
val c = a ++ b

In the example above, the key of 2 is used in both Maps, and our resulting Map c will end up as:

Map(1 -> "a", 2 -> "e", 3 -> "c", 4 -> "d", 5 -> "f")

The value for key 2 will be “e”.

That makes sense and is likely the expected result. And if the values in our Maps are Iterables of some sort, we would expect the same behavior.

But what if we want to not only combine the Maps but (if there are duplicate keys) combine the Iterables as well? We can create this method:

def combineMapsOfIterables[K, V](a: Map[K, Iterable[V]], b: Map[K, Iterable[V]]): Map[K, Iterable[V]] = {
  a ++ { case (k, v) => k -> (v ++ a.getOrElse(k, Iterable.empty)) }

It takes in two Maps of iterables and concatenates them, but Map b is first mapped. For each key/value pair, the same key is used, and the value Iterable is then concatenated with the corresponding Iterable from Map a or an empty Iterable (if Map a doesn’t have the key).

Here’s a simple example with only one key:

val a = Map(1 -> Vector("a", "b", "c"))
val b = Map(1 -> Vector("d", "e", "f"))
val c = combineMapsOfIterables[Int, String](a, b)

Running this would give the resulting Map c as:

Map(1 -> Vector("d", "e", "f", "a", "b", "c"))

That’s it. This simple method allows for combining Maps of Iterables without losing information from either Map.