2010-08-07 14 views
8

En Scala Me gustaría ser capaz de escribirScala Inmutable MultiMap

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

El subyacente mapa [dueño, fije [Mascotas]] debe tener tanto la correspondencia y del inmutable. Aquí está un primer borrador para ImmutibleMultiMap con objeto acompañante:

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? 
    } 
} 

¿Se puede resolver la línea de comentario con elegancia? Tanto el mapa como los conjuntos deben volverse inmutables.

Gracias!

+1

Esto puede ser útil como un ejemplo de cómo convertir un mutable a un mapa inmutable: http://stackoverflow.com/questions/2817055/ converting-mutable-to-inmutable-map –

Respuesta

3

Tiene un problema mayor que eso, porque no hay ningún método en ImmutableMultiMap que devuelva ImmutableMultiMap - por lo tanto, es imposible agregarle elementos, y el constructor no proporciona soporte para crearlo con elementos. Consulte las implementaciones existentes y preste atención al objeto complementario builder y a los métodos relacionados.

+0

Gracias Daniel. Intenté y descifré las cosas del constructor, caminando por el código fuente del objeto compañero hasta inmutable.HashSet. Sin embargo, no puedo encontrarle sentido. ¿Te importaría mostrarme cómo resolverías el problema de construir el ImmutableMultiMap deseado? – PerfectTiling

4

He reescrito este mismo método dos veces ahora, en trabajos sucesivos. :) Alguien realmente Oughta lo hace más general. También es útil tener una versión completa.

/** 
    * 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) 
    } 

Y lo usa así:

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

Utilicé esta respuesta muchas veces. Tenga en cuenta que 'Seq' es más general que list y simplemente requiere cambiar las firmas y convertir' c :: appendTo' para que sea 'c +: seq'. Creo que la respuesta se mejoraría actualizando a 'Seq'? – simbo1905