tags:

views:

211

answers:

1

In Scala 2.8, calling groupBy() on a collection returns a Map where the values are collections, but I want a MultiMap. What's the easiest way to do the conversion? Can I avoid creating a new MultiMap and copying everything over?

+2  A: 

I think the answer to "Do I have to create a new object to mix in a Scala trait?" is "Yes". You can minimize the pain some with wrapping objects and implicit conversions.


For your specific problem, I was unable to coerce groupBy(...) to return a mutable map to mutable sets, which you would need to wrap it with "MapProxy with MultiMap". But, it is not too many lines of code to implement your own version of "groupBy":

package blevins.example

object App extends Application {

  implicit def multiMapable[B](c: Iterable[B]) = new {
    def groupByMM[A](f: B => A) = {
      import scala.collection.mutable._
      val ret = new HashMap[A,Set[B]] with MultiMap[A,B]
      for (e <- c) { ret.addBinding(f(e), e) }
      ret
    } 
  }

  val c = List(1,2,3,4,5,6,7,8,9)
  val mm = c.groupByMM { i => if (i < 5) "alpha" else "beta" }
  mm.addBinding("alpha",12)
  println(mm) // Map(beta -> Set(5, 7, 6, 9, 8), alpha -> Set(3, 1, 4, 2, 12))

}


Addendum

Here is an example of wrapping an existing Map[String,Set[Int]] into a MultiMap without copying the values:

object App extends Application {
  import scala.collection.mutable._
  val seed: Map[String,Set[Int]] = Map("even" -> Set(2,4,6), "odd" -> Set(1,3,5))

  val multiMap = new MapProxy[String,Set[Int]] with MultiMap[String,Int] {
    val self = seed
  }

  multiMap.addBinding("even", 8)
  println(multiMap) // Map(odd -> Set(5, 3, 1), even -> Set(6, 8, 4, 2))
}

Note that this cannot be done on the result of groupBy(...) because the seed map is required to be mutable and groupBy(...) returns an immutable map.

Mitch Blevins
Calling groupBy() and a mutable collection does return a mutable.Map.
Craig P. Motlin
Can you demonstrate groupBy returning a mutable Map? I get a compiler error like shown here (http://gist.github.com/245062).
Mitch Blevins
I guess it returns an immutable map of mutable sets? Regardless, I don't want a map at all, I want a multimap.
Craig P. Motlin
The mutability of the result of groupBy(...) is relevant because wrapping it to create a "MapProxy with MultiMap" without copying the values requires it be mutable. See appended code in answer.
Mitch Blevins
Thank you, the addendum is exactly the info I was looking for.
Craig P. Motlin