shortcut for groupBy(_._1).mapValues(_.map(_._2))

1,777 views
Skip to first unread message

Andy Coolware

unread,
Oct 25, 2012, 3:18:51 PM10/25/12
to scala-user
Hi,

I use Scala collections very extensively and realized that I use that
idiom very often: groupBy(_._1).mapValues(_.map(_._2)).

I wonder if there is a nicer way to code it up?

Thx,
Andy

David Ross

unread,
Oct 26, 2012, 3:21:04 AM10/26/12
to scala...@googlegroups.com
This *could* be implemented as a general method on Traversables in the same way "toMap" is. Here is a helper method on lists that shows the idea:

def toMultimap[A, T, U](list: List[A])(implicit ev: A <:< (T, U)): Map[T, List[U]] =
list.groupBy(_._1).mapValues(_.map(_._2))

Thoughts?

Dennis Haupt

unread,
Oct 26, 2012, 3:33:23 AM10/26/12
to David Ross, scala...@googlegroups.com
+1, except that mapValues is just a view. you'd expect a real new collection

-------- Original-Nachricht --------
> Datum: Fri, 26 Oct 2012 00:21:04 -0700 (PDT)
> Von: David Ross <dyr...@klout.com>
> An: scala...@googlegroups.com
> Betreff: [scala-user] Re: shortcut for groupBy(_._1).mapValues(_.map(_._2))

Som Snytt

unread,
Oct 26, 2012, 12:10:35 PM10/26/12
to Dennis Haupt, David Ross, scala...@googlegroups.com
On Fri, Oct 26, 2012 at 12:33 AM, Dennis Haupt <h-s...@gmx.de> wrote:

> This *could* be implemented as a general method on Traversables

That was my idea, too, but as an extension method, it didn't take me five minutes to mash-up groupBy and toMap.  HasNewBuilder.newBuilder is protected[this].

package object groupmap {

  import scala.collection.{ mutable, immutable, TraversableLike }
  import scala.collection.generic.CanBuildFrom

  implicit class Grouper[A, Repr](val c: TraversableLike[A, Repr]) extends AnyVal {

    def newBuilder[Elem, To] = {
      import scala.reflect.runtime.{ currentMirror => cm, universe => ru }
      //val im = cm.reflect(c) // bad kind
      val im = (ru runtimeMirror getClass.getClassLoader) reflect c
      val nb = ru.newTermName("newBuilder")
      val mem = im.symbol.typeSignature.members.find(_.name == nb).get.asMethod
      val mm = im reflectMethod mem
      mm().asInstanceOf[mutable.Builder[Elem, To]]
    }

    def toGroupMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, Repr] = {
      val m = mutable.Map.empty[T, mutable.Builder[U, Repr]]
      for (elem <- c) {
        val p = elem.asInstanceOf[Pair[T, U]]
        val bldr = m.getOrElseUpdate(p._1, newBuilder[U, Repr])
        bldr += p._2
      }
      val b = immutable.Map.newBuilder[T, Repr]
      for (Pair(k, v) <- m)
        b += Pair(k, v.result)

      b.result
    }
  }
}
package groupmap {
  object Test extends App {
    val data = List((1,"one"),(1,"un"),(2,"zwei"),(3,"three"),(3,"trois"),(2,"deux"),(2,"due"))

    val shot = (data groupBy (_._1)) mapValues (_ map (_._2))
    println(s"First shot ${shot}")

    val res = data.toGroupMap[Int, String]
    println(s"Best shot ${res}")
  }
}

Reply all
Reply to author
Forward
0 new messages