3

val pairs: Iterable[Pair[Key, Value]]いくつかのキー=>値のペアを持つiterableがあります。

Map[Key, Iterable[Value]]ここで、各キーに対してIterable指定されたキーのすべての値を持つを作成したいと思いますpairs。(実際には は必要ありません。SeqどれでもかまいませんIterable)。

Mapmutableおよび/またはmutable を使用してそれを行うことができますListBuffer

しかし、誰もが「正しい」スカラは可変コレクションを使用しないことだと言っています。それで、不変のコレクションでのみこれを行うことは可能ですか? (たとえば、 、 などをmap使用foldLeft)

4

5 に答える 5

5

これを行うための本当に簡単な方法を見つけました

pairs.groupBy{_._1}.mapValues{_.map{_._2}}

以上です。

于 2012-07-02T21:47:01.563 に答える
4

非周期的な可変データ構造でできることはすべて、不変データ構造でもできます。トリックは非常に簡単です:

loop -> recursion or fold
mutating operation -> new-copy-with-change-made operation

したがって、たとえば、あなたのケースでは、おそらく をループして、Iterable毎回値を追加しています。便利なトリックを適用すると、

def mkMap[K,V](data: Iterable[(K,V)]): Map[K, Iterable[V]] = {
  @annotation.tailrec def mkMapInner(
    data: Iterator[(K,V)],
    map: Map[K,Vector[V]] = Map.empty[K,Vector[V]]
  ): Map[K,Vector[V]] = {
    if (data.hasNext) {
      val (k,v) = data.next
      mkMapInner(data, map + (k -> map.get(k).map(_ :+ v).getOrElse(Vector(v))))
    }
    else map
  }
  mkMapInner(data.iterator)
}

ここでは、再帰的な内部メソッドを宣言することによってループ置換を実装することを選択しました (@annotation.tailrec を使用して、再帰が while ループに最適化されていることを確認し、スタックが壊れないようにします)。

テストしてみましょう:

val pairs = Iterable((1,"flounder"),(2,"salmon"),(1,"halibut"))
scala> mkMap(pairs)
res2: Map[Int,Iterable[java.lang.String]] = 
      Map(1 -> Vector(flounder, halibut), 2 -> Vector(salmon))

さて、Scala のコレクション ライブラリにも、これに役立つものが含まれていることがわかりました。

scala> pairs.groupBy(_._1).mapValues{ _.map{_._2 } }

groupBy主要なメソッドであり、残りはそれが生成するものを必要な形にクリーンアップします。

于 2012-07-02T21:46:02.827 に答える
3

記録のために、これは foldでかなりきれいに書くことができます。あなたPairが標準ライブラリ(別名Tuple2)のものであると仮定します:

pairs.foldLeft(Map.empty[Key, Seq[Value]]) {
  case (m, (k, v)) => m.updated(k, m.getOrElse(k, Seq.empty) :+ v)
}

もちろん、この場合、groupByアプローチの方が便利です。

于 2012-07-02T22:11:30.893 に答える
1
val ps = collection.mutable.ListBuffer(1 -> 2, 3 -> 4, 1 -> 5)

ps.groupBy(_._1).mapValues(_ map (_._2))
  // = Map(1 -> ListBuffer(2, 5), 3 -> ListBuffer(4))

これにより、出力マップにミュータブルが表示されます。 ListBuffer出力を不変にしたい場合(これがあなたが求めているものかどうかわからない場合)、次を使用しますcollection.breakOut

ps.groupBy(_._1).mapValues(_.map(_._2)(collection.breakOut))
   // = Map(1 -> Vector(2, 5), 3 -> Vector(4))

Vectorのデフォルトのように見えますbreakOutが、確かに、左側で戻り値の型を指定できます: val myMap: Map[Int,Vector[Int]] = ...

ブレイクアウトの詳細については、こちらをご覧ください

方法として:

def immutableGroup[A,B](xs: Traversable[(A,B)]): Map[A,Vector[B]] =
  xs.groupBy(_._1).mapValues(_.map(_._2)(collection.breakOut))
于 2012-07-03T02:43:17.580 に答える
0

私はこの関数を頻繁に実行するので、これをgroupByKey正確に実行する暗黙の呼び出しがあります。

class EnrichedWithGroupByKey[A, Repr <: Traversable[A]](self: TraversableLike[A, Repr]) {
  def groupByKey[T, U, That](implicit ev: A <:< (T, U), bf: CanBuildFrom[Repr, U, That]): Map[T, That] =
    self.groupBy(_._1).map { case (k, vs) => k -> (bf(self.asInstanceOf[Repr]) ++= vs.map(_._2)).result }
}
implicit def enrichWithGroupByKey[A, Repr <: Traversable[A]](self: TraversableLike[A, Repr]) = new EnrichedWithGroupByKey[A, Repr](self)

そして、あなたはそれを次のように使用します:

scala> List(("a", 1), ("b", 2), ("b", 3), ("a", 4)).groupByKey
res0: Map[java.lang.String,List[Int]] = Map(a -> List(1, 4), b -> List(2, 3))

マップをすぐに実行するのではなく、ビューを作成するため.map { case (k, vs) => k -> ... }の代わりに使用することに注意してください。これらの値に何度もアクセスする予定がある場合は、毎回再計算することを意味するため、ビューアプローチは避けたほうがよいでしょう。mapValuesmapValues.map(_._2)

于 2012-07-03T04:39:03.383 に答える