以下のようなマップをマージするにはどうすればよいですか:
Map1 = Map(1 -> Class1(1), 2 -> Class1(2))
Map2 = Map(2 -> Class2(1), 3 -> Class2(2))
統合後。
Merged = Map( 1 -> List(Class1(1)), 2 -> List(Class1(2), Class2(1)), 3 -> Class2(2))
リスト、セット、またはサイズ属性を持つその他のコレクションを指定できます。
標準ライブラリを使用すると、次のように実行できます。
// convert maps to seq, to keep duplicate keys and concat
val merged = Map(1 -> 2).toSeq ++ Map(1 -> 4).toSeq
// merged: Seq[(Int, Int)] = ArrayBuffer((1,2), (1,4))
// group by key
val grouped = merged.groupBy(_._1)
// grouped: scala.collection.immutable.Map[Int,Seq[(Int, Int)]] = Map(1 -> ArrayBuffer((1,2), (1,4)))
// remove key from value set and convert to list
val cleaned = grouped.mapValues(_.map(_._2).toList)
// cleaned: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(2, 4))
これは、私が思いつく最も簡単な実装です。
val m1 = Map(1 -> "1", 2 -> "2")
val m2 = Map(2 -> "21", 3 -> "3")
def merge[K, V](m1:Map[K, V], m2:Map[K, V]):Map[K, List[V]] =
(m1.keySet ++ m2.keySet) map { i => i -> (m1.get(i).toList ::: m2.get(i).toList) } toMap
merge(m1, m2) // Map(1 -> List(1), 2 -> List(2, 21), 3 -> List(3))
次を使用できます scalaz :
_import scalaz._, Scalaz._
val m1 = Map('a -> 1, 'b -> 2)
val m2 = Map('b -> 3, 'c -> 4)
m1.mapValues{List(_)} |+| m2.mapValues{List(_)}
// Map('b -> List(2, 3), 'c -> List(4), 'a -> List(1))
_
Set
sをMap
の値として取得するには、Set(_)
の代わりにList(_)
を使用できます。
Semigroupを参照してください scalaz cheat sheet (または learning scalaz )_|+|
_演算子の詳細。
Int
の場合_|+|
_は_+
_として機能し、List
の場合-_++
_として、Map
の場合は_|+|
_に適用されます同じキーの値。
これについてのブログ記事を書いたので、チェックしてみてください。
http://www.nimrodstech.com/scala-map-merge/
基本的にscalazセミグループを使用すると、これを非常に簡単に達成できます
次のようになります。
import scalaz.Scalaz._
Map1 |+| Map2
cats を使用した、クリーンな方法の1つです。
import cats.implicits._
Map(1 -> "Hello").combine(Map(2 -> "Goodbye"))
//Map(2 -> Goodbye, 1 -> Hello)
両方のマップが同じタイプでなければならないことに注意することが重要です(この場合、Map[Int, String]
)。
長い説明:
combine
は、実際にはMapのメンバーではありません。 cats.implicitsをインポートすることにより、簡潔な構文を有効にするいくつかの暗黙的なクラスとともに、catsのMap組み込みモノイドインスタンスをスコープに入れます。
上記はこれと同等です:
Monoid[Map[Int, String]].combine(Map(1 -> "Hello"), Map(2 -> "Goodbye"))
Monoid "summoner" function を使用してMonoid [Map [Int、String]]インスタンスをスコープ内で取得し、その結合関数を使用している場合。
FoldLeftを使用して、同じタイプの2つのマップをマージできます
def merge[A, B](a: Map[A, B], b: Map[A, B])(mergef: (B, Option[B]) => B): Map[A, B] = {
val (big, small) = if (a.size > b.size) (a, b) else (b, a)
small.foldLeft(big) { case (z, (k, v)) => z + (k -> mergef(v, z.get(k))) }
}
def mergeIntSum[A](a: Map[A, Int], b: Map[A, Int]): Map[A, Int] =
merge(a, b)((v1, v2) => v2.map(_ + v1).getOrElse(v1))
例:
val a = Map("a" -> 1, "b" -> 5, "c" -> 6)
val b = Map("a" -> 4, "z" -> 8)
mergeIntSum(a, b)
res0: Map[String,Int] = Map(a -> 5, b -> 5, c -> 6, z -> 8)
Scala 2.13
を開始して、標準ライブラリのみに基づく別のソリューションは、 groupMap
を使用することで構成されます。これは(名前が示すように)groupBy
に続くmapValues
と同等です。
// val m1 = Map(1 -> "a", 2 -> "b")
// val m2 = Map(2 -> "c", 3 -> "d")
(m1.toSeq ++ m2).groupMap(_._1)(_._2)
// Map[Int,Seq[String]] = Map(2 -> List("b", "c"), 1 -> List("a"), 3 -> List("d"))
この:
2つのマップを一連のタプル(List((1,"a"), (2,"b"), (2,"c"), (3,"d"))
)として連結します。簡潔にするために、m2
はimplicitlySeq
に変換されてm1.toSeq
のタイプに適応しますが、m2.toSeq
を使用して明示的にすることもできます。
最初のタプル部分(_._1
)に基づくgroup
s要素(group Mapのグループ部分)
map
sは、値を2番目のタプル部分(_._2
)にグループ化します(グループのマップ部分Map)
2つのマップを結合するソリューション:Map[A,B]
、結果タイプ:Map[A,List[B]]
Scala Cats(@David Castilloが提供するわずかに改善されたバージョン)経由)
//元の各マップをMap [A、List [B]]に変換します。 // Monoid [List]のインスタンスをスコープに追加して、リストを結合します。
import cats.instances.map._ // for Monoid
import cats.syntax.semigroup._ // for |+|
import cats.instances.list._
val map1 = Map("a" -> 1, "b" -> 2)
.mapValues(List(_))
val map2 = Map("b" -> 3, "d" -> 4)
.mapValues(List(_))
map1 |+| map2
元のマップをいじりたくない場合は、次のようなことをすることができます
val target = map1.clone()
val source = map2.clone()
source.foreach(e => target += e._1 -> e._2)
m2.foldLeft(m1.mapValues{List[CommonType](_)}) { case (acc, (k, v)) =>
acc.updated(k, acc.getOrElse(k, List.empty) :+ v)
}
Jwvhで述べたように、Class1がClass2の上限型ではない場合、List型を明示的に指定する必要があります。 CommonTypeは、Class1とClass2の両方の上限であるタイプです。
Scala= scala-collection-contrib というモジュールがあり、mergeByKey
のような非常に便利なメソッドを提供します。
最初に、build.sbt
に追加の依存関係を追加する必要があります。
libraryDependencies += "org.scala-lang.modules" %% "scala-collection-contrib" % "0.1.0"
そして、次のようにマージすることができます:
import scala.collection.decorators._
val map1 = Map(1 -> Class1(1), 2 -> Class1(2))
val map2 = Map(2 -> Class2(1), 3 -> Class2(2))
map1.mergeByKeyWith(map2){
case (a,b) => a.toList ++ b.toList
}