5

Scala 2.8では、キーごとに複数の値を持つ不変のマップがあります。

Map[T,Iterable[U]]

優れた表現はありますか?次に、このようなマップをどのように生成しますか

Iterable[(T,U)]

?私は現在使用しています:

def toGroupedMap[T,U](vals: Iterable[(T,U)]): Map[T,Iterable[U]] =
  vals.groupBy(_._1).map({ case (s,it) => (s,it.map(_._2)) }).toMap

これは機能しますが、不格好に感じます。

編集:不変のデータを使用していることを指定する必要があります。MultiMapに相当する不変のものはありますか?

4

3 に答える 3

4

不変性が本当に必要ない場合は、他の人が言っているMultiMapように、それが進むべき道です。本当に不変性が必要な場合、あなたが取ったアプローチは他の何よりも簡単です。(AFAIK)には何も組み込まれておらず、不変のMultiMapを作成するには、そこにある方法よりもはるかに多くの作業が必要になります。

表現が優れているかどうかは、使用法によって異なります。1つのキーに対応するすべての値で何かをしたいことがよくありますか?同じ値をマップに複数回挿入できますか?両方に「はい」の場合、あなたの表現は正しいものです。

同じ値を1つのキーに最大1回挿入する場合は、Set[U]代わりにを使用する必要があります(これはに追加するIterable[U]ことで簡単に実行できます)。.toSetit.map(_._2)

セット/イテレータを処理する必要がなく、それを我慢している場合(つまり、キーとセットの値のペアではなく、キーと値のペアを使用したい場合)、マップの周りにラッパークラスを作成する必要があります。単一のマップインターフェイスを提供し、+、-、およびイテレータを使用して正しいことを行います。

これは、私が予想していたよりも少し長くなった例です(ここでは、REPLにカットアンドペーストするためにフォーマットされています)。

import scala.collection._
class MapSet[A,B](
  val sets: Map[A,Set[B]] = Map[A,Set[B]]()
) extends Map[A,B] with MapLike[A,B,MapSet[A,B]] {
  def get(key: A) = sets.getOrElse(key,Set[B]()).headOption
  def iterator = new Iterator[(A,B)] {
    private val seti = sets.iterator
    private var thiskey:Option[A] = None
    private var singles:Iterator[B] = Nil.iterator
    private def readyNext {
      while (seti.hasNext && !singles.hasNext) {
        val kv = seti.next
        thiskey = Some(kv._1)
        singles = kv._2.iterator
      }
    }
    def hasNext = {
      if (singles.hasNext) true
      else {
        readyNext
        singles.hasNext
      }
    }
    def next = {
      if (singles.hasNext) (thiskey.get , singles.next)
      else {
        readyNext
        (thiskey.get , singles.next)
      }
    }
  }
  def +[B1 >: B](kv: (A,B1)):MapSet[A,B] = {
    val value:B = kv._2.asInstanceOf[B]
    new MapSet( sets + ((kv._1 , sets.getOrElse(kv._1,Set[B]()) + value)) )
  }
  def -(key: A):MapSet[A,B] = new MapSet( sets - key )
  def -(kv: (A,B)):MapSet[A,B] = {
    val got = sets.get(kv._1)
    if (got.isEmpty || !got.get.contains(kv._2)) this
    else new MapSet( sets + ((kv._1 , got.get - kv._2)) )
  }
  override def empty = new MapSet( Map[A,Set[B]]() )
}

そして、これが次のように希望どおりに機能することがわかります。

scala> new MapSet() ++ List(1->"Hi",2->"there",1->"Hello",3->"Bye")
res0: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 3 -> Bye)

scala> res0 + (2->"ya")
res1: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 2 -> ya, 3 -> Bye)

scala> res1 - 1
res2: scala.collection.Map[Int,java.lang.String] = Map(2 -> there, 2 -> ya, 3 -> Bye)

(ただし、++の後にMapSetを取得したい場合は、++をオーバーライドする必要があります。マップ階層には、このようなことを処理する独自のビルダーがありません)。

于 2010-02-03T19:58:20.957 に答える
2

MapのMultiMapミックスインを調べてください。

于 2010-02-03T17:22:52.873 に答える
0

マルチマップはあなたが必要とするものです。これは、リストを作成し、List [(String、Int)]からエントリを追加する例です。もっときれいな方法があると確信しています。

scala> val a = new collection.mutable.HashMap[String, collection.mutable.Set[Int]]() with collection.mutable.MultiMap[String, Int]
a: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int] = Map()

scala> List(("a", 1), ("a", 2), ("b", 3)).map(e => a.addBinding(e._1, e._2))                                                      
res0: List[scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int]] = List(Map(a -> Set(1, 2), b -> Set(3)), Map(a -> Set(1, 2), b -> Set(3)), Map(a -> Set(1, 2), b -> Set(3)))

scala> a("a")
res2: scala.collection.mutable.Set[Int] = Set(1, 2)
于 2010-02-03T17:41:57.107 に答える