Неизменяемая карта Scala MultiMap

В Scala я хотел бы написать

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

Базовая карта [Owner, Set [Pet]] должна иметь как Map, так и Set неизменными. Вот первый черновик ImmutibleMultiMap с сопутствующим объектом:

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

Можете ли вы элегантно разрешить строку комментария? И карта, и наборы должны стать неизменными.

Спасибо!


person PerfectTiling    schedule 07.08.2010    source источник
comment
Это может быть полезно в качестве примера того, как преобразовать изменяемую карту в неизменяемую: stackoverflow.com/questions/2817055/   -  person Arjan Blokzijl    schedule 08.08.2010


Ответы (2)


Я уже дважды переписывал этот же метод на разных работах. :) Кто-нибудь Really Oughta делает это более общим. Также удобно иметь полную версию.

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

И вы используете это так:

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)) 
person Alex Cruise    schedule 16.12.2010
comment
Использовал этот ответ много раз. Обратите внимание, что Seq является более общим, чем список, и просто требует изменения подписей и преобразования c :: appendTo в c +: seq. Думаю, ответ можно улучшить, перейдя на Seq? - person simbo1905; 12.12.2015

У вас есть более серьезная проблема, потому что в ImmutableMultiMap нет метода, который возвращал бы ImmutableMultiMap, поэтому к нему невозможно добавлять элементы, а конструктор не обеспечивает поддержку для его создания с элементами. Просмотрите существующие реализации и обратите внимание на builder сопутствующего объекта и связанные с ним методы.

person Daniel C. Sobral    schedule 07.08.2010
comment
Спасибо, Даниэль. Я попытался расшифровать материал конструктора, пройдя по исходному коду сопутствующего объекта в immutable.HashSet. Однако я не могу понять этого. Не могли бы вы показать мне, как вы решите проблему построения желаемого ImmutableMultiMap? - person PerfectTiling; 08.08.2010