2010-08-07 8 views
8

In Scala würde ich in der Lage sein möchten, schreibenScala Immutable MultiMap

val petMap = ImmutableMultiMap(Alice->Cat, Bob->Dog, Alice->Hamster) 

Die zugrunde liegende Map [Besitzer, Set [Pet]] haben, sollten beide Karten und unveränderlich fest. Hier ist ein erster Entwurf für ImmutibleMultiMap mit Begleitobjekt:

import collection.{mutable,immutable} 

class ImmutableMultiMap[K,V] extends immutable.HashMap[K,immutable.Set[V]] 

object ImmutableMultiMap { 
    def apply[K,V](pairs: Tuple2[K,V]*): ImmutableMultiMap[K,V] = { 
    var m = new mutable.HashMap[K,mutable.Set[V]] with mutable.MultiMap[K,V] 
    for ((k,v) <- pairs) m.addBinding(k,v) 
    // How do I return the ImmutableMultiMap[K,V] corresponding to m here? 
    } 
} 

Können Sie die Kommentarzeile elegant lösen? Sowohl die Karte als auch die Mengen sollten unveränderlich werden.

Danke!

+1

Dies als Beispiel dafür, wie nützlich sein kann, eine veränderbare zu einer unveränderlichen Karte zu konvertieren: http://stackoverflow.com/questions/2817055/ Converting-Mutable-to-immable-Karte –

Antwort

3

Sie haben ein größeres Problem, weil es keine Methode in ImmutableMultiMap gibt, die eine ImmutableMultiMap zurückgibt - daher ist es unmöglich, Elemente hinzuzufügen, und der Konstruktor bietet keine Unterstützung für die Erstellung mit Elementen. Bitte beachten Sie die vorhandenen Implementierungen und achten Sie auf builder des Companion-Objekts und verwandte Methoden.

+0

Danke Daniel. Ich habe versucht, das Builder-Zeug zu entschlüsseln, indem ich den Quellcode für das Companion-Objekt zu immutable.HashSet durchging. Ich kann jedoch keinen Sinn daraus machen. Würde es Ihnen etwas ausmachen, mir zu zeigen, wie Sie das Problem der Konstruktion der gewünschten ImmutableMultiMap lösen würden? – PerfectTiling

4

Ich habe diese Methode jetzt zweimal bei aufeinanderfolgenden Jobs umgeschrieben. :) Jemand wirklich Oughta macht es allgemeiner. Es ist praktisch, eine vollständige Version zu haben.

/** 
    * Like {@link scala.collection.Traversable#groupBy} but lets you return both the key and the value for the resulting 
    * Map-of-Lists, rather than just the key. 
    * 
    * @param in the input list 
    * @param f the function that maps elements in the input list to a tuple for the output map. 
    * @tparam A the type of elements in the source list 
    * @tparam B the type of the first element of the tuple returned by the function; will be used as keys for the result 
    * @tparam C the type of the second element of the tuple returned by the function; will be used as values for the result 
    * @return a Map-of-Lists 
    */ 
    def groupBy2[A,B,C](in: List[A])(f: PartialFunction[A,(B,C)]): Map[B,List[C]] = { 

    def _groupBy2[A, B, C](in: List[A], 
          got: Map[B, List[C]], 
          f: PartialFunction[A, (B, C)]): Map[B, List[C]] = 
    in match { 
     case Nil => 
     got.map {case (k, vs) => (k, vs.reverse)} 

     case x :: xs if f.isDefinedAt(x) => 
     val (b, c) = f(x) 
     val appendTo = got.getOrElse(b, Nil) 
     _groupBy2(xs, got.updated(b, c :: appendTo), f) 

     case x :: xs => 
     _groupBy2(xs, got, f) 
    } 

    _groupBy2(in, Map.empty, f) 
    } 

Und verwenden Sie es wie folgt aus:

val xs = (1 to 10).toList 
groupBy2(xs) { 
    case i => (i%2 == 0, i.toDouble) 
} 

res3: Map[Boolean,List[Double]] = Map(false -> List(1.0, 3.0, 5.0, 7.0, 9.0),  
             true -> List(2.0, 4.0, 6.0, 8.0, 10.0)) 
+0

Diese Antwort wurde oft verwendet. Beachten Sie, dass "Seq" allgemeiner als die Liste ist und nur das Ändern der Signaturen und das Konvertieren von "c :: appendTo" in "c +: seq" erfordert. Ich denke, dass die Antwort verbessert werden würde, wenn man auf "Seq" upgraden würde. – simbo1905