1. ホーム
  2. scala

[解決済み] 2つのマップをマージし、同じキーの値を合計するための最良の方法?

2022-04-20 14:26:59

質問

val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

それらをマージして、同じキーの値を合計したいのです。その結果、次のようになる。

Map(2->20, 1->109, 3->300)

これで2つの解決策ができました。

val list = map1.toList ++ map2.toList
val merged = list.groupBy ( _._1) .map { case (k,v) => k -> v.map(_._2).sum }

そして

val merged = (map1 /: map2) { case (map, (k,v)) =>
    map + ( k -> (v + map.getOrElse(k, 0)) )
}

しかし、もっと良い解決策があれば知りたい。

どのように解決するのですか?

スカラ座 という概念があります。 セミグループ これは、あなたがここでやりたいことをキャプチャし、間違いなく最も短く、最もきれいな解決策につながるものです。

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val map1 = Map(1 -> 9 , 2 -> 20)
map1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 9, 2 -> 20)

scala> val map2 = Map(1 -> 100, 3 -> 300)
map2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 100, 3 -> 300)

scala> map1 |+| map2
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 109, 3 -> 300, 2 -> 20)

具体的には、二項演算子で Map[K, V] はマップのキーを結合し V の半群演算子で重複する値を処理します。 の標準的な半グループは Int は加算演算子を使うので、重複するキーごとに値の合計が得られます。

編集 : user482745さんのリクエストにより、もう少し詳しく。

数学的に 半グループ は、単に値の集合と、その集合から2つの値を取り、その集合から別の値を生成する演算子です。 ですから、足し算の整数は半群になります。 + 演算子は2つのintを組み合わせて別のintを作る。

また、2つのマップを組み合わせて、何らかの形で2つの入力の組み合わせである新しいマップを生成する操作を思いつく限り、"与えられたキー型と値型を持つすべてのマップ"のセットに対する半グループを定義することも可能です。

両方のマップに現れるキーがない場合、これは些細なことです。 両方のマップに同じキーが存在する場合、そのキーが対応する2つの値を結合する必要があります。 同じ型の2つの実体を結合する演算子を説明しただけではないか? というわけで、Scalazでは Map[K, V] のための半グループが存在する場合に限り、存在する。 V が存在する-。 V の半グループは、同じキーに割り当てられている2つのマップの値を結合するために使われます。

というのは Int が値型であるため、この例では 1 キーは、マッピングされた2つの値の整数加算によって解決されます(Intの半群演算子がそうであるように)、したがって 100 + 9 . もし値がStringであれば、衝突は2つのマッピングされた値の文字列連結という結果になります(これもStringの半グループ演算子がそうするため)。

(そして興味深いことに、文字列の連結は可換でないため、つまりは "a" + "b" != "b" + "a" - その結果、半群演算もそうではありません。 つまり map1 |+| map2 とは異なります。 map2 |+| map1 はStringの場合ですが、Intの場合はそうではありません)。