3

What is the best way in scala to merge two mutable maps of mutable sets? The operation must be commutative. The things I've tried seem ugly...

import scala.collection.mutable
var d1 = mutable.Map[String, mutable.SortedSet[String]]()
var d2 = mutable.Map[String, mutable.SortedSet[String]]()

// adding some elements.  Accumulating nimals with the set of sounds they make.
d1.getOrElseUpdate("dog", mutable.SortedSet[String]("woof"))
d2.getOrElseUpdate("cow", mutable.SortedSet[String]("moo"))
d2.getOrElseUpdate("dog", mutable.SortedSet[String]("woof", "bark"))

magic (that is commutative!)

scala.collection.mutable.Map[String,scala.collection.mutable.SortedSet[String]] =
Map(dog -> TreeSet(bark, woof), cow -> TreeSet(moo))

Basically, I want to override the definition for ++ to merge the sets for matching map keys. Notice that d1 ++ d2 gives the right answer, while d2 ++ d1 does not (so ++ is not commutative here).

2
  • Why mutable? Do you want to update one of the maps with the values from the other?
    – Kolmar
    Commented May 4, 2016 at 15:08
  • Thinking about it, I probably could use immutable too. Mutable just made more sense for how I was using this. See edit
    – bill_e
    Commented May 4, 2016 at 15:16

1 Answer 1

3

For every key that would be in the resulting Map, you have to merge (++) the value Sets from d1 and d2 for that key.

For mutable.Maps and mutable.Sets, when you are updating one of the Maps, the implementation is really straightforward:

for ((key, values) <- d2) 
  d1.getOrElseUpdate(key, mutable.SortedSet.empty) ++= values

You can actually create an empty mutable.Map, and use that code to update it with d1 and d2 (and other Maps if needed) in any order.

You can wrap this operation in a following function:

val d1 = mutable.Map[String, mutable.SortedSet[String]](
  "dog" -> mutable.SortedSet("woof"), 
  "cow" -> mutable.SortedSet("moo"))
val d2 = mutable.Map[String, mutable.SortedSet[String]](
  "dog" -> mutable.SortedSet("woof", "bark"))

def updateMap[A, B : Ordering]( // `Ordering` is a requirement for `SortedSet`
  d1: mutable.Map[A, mutable.SortedSet[B]])(
  // `Iterable`s are enough here, but allow to pass a `Map[A, Set[B]]`
  d2: Iterable[(A, Iterable[B])] 
): Unit =
  for ((key, values) <- d2)
    d1.getOrElseUpdate(key, mutable.SortedSet.empty) ++= values

// You can call 
// `updateMap(d1)(d2)` or 
// `updateMap(d2)(d1)` to achieve the same result (but in different variables)

For immutable Maps one possible implementation is:

(
  for (key <- d1.keySet ++ d2.keySet)
  yield key -> (d1.getOrElse(key, Set.empty) ++ d2.getOrElse(key, Set.empty))
).toMap

Other, likely more effective, but probably slightly more complex implementations are also possible.

Not the answer you're looking for? Browse other questions tagged or ask your own question.