2016-03-22 10 views
1

Ich habe eine Seq[(A, B)]. Ich wollte eine implizite Methode zu solchen Sammlungen hinzufügen, so dass ich .toMultiMap tun kann, um eine Map[A, Seq[B]] zurück zu bekommen.Generisch erweiternden Scala Kollektionen

Dies war mein erster Versuch:

implicit class PairsExtensions[A, B](t: Traversable[(A, B)]) { 
    def toMultiMap: Map[A, Traversable[B]] = t.groupBy(_._1).mapValues(_.map(_._2)) 
    } 

Aber jetzt das Problem ist, ich für die Werte ein Traversable wieder immer. Ich möchte ein Map[A, Set[B]] zurückbekommen, wenn ich Set[(A, B)].toMultiMap mache. So

, dann habe ich versucht, so etwas wie diese:

implicit class PairsExtensions2[A, B, Repr[_] <: Traversable[(A, B)]](t: TraversableLike[(A, B), Repr[(A, B)]]) { 
    def toMultiMap(implicit bf: CanBuild[B, Repr[B]]): Map[A, Repr[B]] = t.groupBy(_._1).mapValues(_.map(_._2)) 
    } 

Aber es funktioniert nicht:

val m1: Map[Int, Set[String]] = Set.empty[(Int, String)] 
val m2: Map[Int, List[String]] = List.empty[(Int, String)] 

Was ist die Art und Weise, dies zu tun?

+0

@ m-ZL Sorry, klar zu sein, der erste Versuch, kompiliert und arbeitet. Zweites Snippet kompiliert nicht. Ich habe gerade meinen Versuch gepostet. – pathikrit

+0

Nur meine 2 Cent: Sie sollten vielleicht noch einmal darüber nachdenken, ob es wirklich sinnvoll ist, '.toMultiMap' auf einem' Set' zu verwenden, um Ihnen eine 'Map [A, Set [B]' zu geben. Die Einzigartigkeit der früheren und der späteren Sammlungen wird sich deutlich unterscheiden. – Owen

+0

@Owen: Ich verstehe. Es geht nicht darum, '.toMultiMap' zu erstellen, sondern generische Utils für Scala-Sammlungen zu schreiben. Sie können sich vorstellen, für 'List's will ich' Map [A, List [B]] 'zurück und für' Seq's will ich 'Map [A, Seq [B]]' – pathikrit

Antwort

4

Ich denke, das könnte sein, wonach Sie suchen.

import scala.collection.generic.CanBuildFrom 
import scala.language.higherKinds 

implicit class PairsExtensions[A, B, C[X] <: Traversable[X]](t: C[(A, B)]) { 
    def toMultiMap(implicit cbf: CanBuildFrom[Nothing, B, C[B]]): Map[A, C[B]] = 
    t.groupBy(_._1).mapValues(_.map(_._2).to[C]) 
} 

Dies geht ein paar einfachen Tests:

scala> val m1: Map[Int, Set[String]] = Set.empty[(Int, String)].toMultiMap 
m1: Map[Int,Set[String]] = Map() 

scala> val m2: Map[Int, List[String]] = List.empty[(Int, String)].toMultiMap 
m2: Map[Int,List[String]] = Map() 

scala> Seq(('c',4),('x',2),('c',5)).toMultiMap 
res4: Map[Char,Seq[Int]] = Map(x -> Vector(2), c -> Vector(4, 5)) 
+0

Leicht tangential. Gibt es etwas Ähnliches für Maps, z.B. Ich möchte eine implizite Invertierung hinzufügen, die bei gegebenem 'C [K, V] <: Map [K, V]', ergibt: C [V, Set [K]] '? – pathikrit

+0

Genauer gesagt, ich weiß, wie man eine 'Map [V, Set [K]]' erzeugt, aber ich konnte nicht 'CanBuildFrom' für ein' C' verwenden, das 2 Typparameter benötigt. – pathikrit

+0

Der Kommentarbereich ist ein schlechter Ort, um ein neues (wenn auch verwandtes) Thema zu beginnen, aber ich habe einen 'Map'-Inverter. Es verwandelt eine 'Map [K, Liste [V]]' in eine 'Map [V, Liste [K]]'. Versuchen Sie für einige Map 'm' folgendes:' m flatten {case (k, vs) => vs.map ((_, k))} groupBy (_._ 1) mapValues ​​{_.map (_._ 2)} ' – jwvh