web-dev-qa-db-ja.com

Scala:マージマップ

以下のようなマップをマージするにはどうすればよいですか:

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))

リスト、セット、またはサイズ属性を持つその他のコレクションを指定できます。

33
Robinho

標準ライブラリを使用すると、次のように実行できます。

// 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))
58
drexin

これは、私が思いつく最も簡単な実装です。

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))
22
tiran

次を使用できます 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))
_

SetsをMapの値として取得するには、Set(_)の代わりにList(_)を使用できます。

Semigroupを参照してください scalaz cheat sheet (または learning scalaz )_|+|_演算子の詳細。

Intの場合_|+|_は_+_として機能し、Listの場合-_++_として、Mapの場合は_|+|_に適用されます同じキーの値。

16
senia

これについてのブログ記事を書いたので、チェックしてみてください。

http://www.nimrodstech.com/scala-map-merge/

基本的にscalazセミグループを使用すると、これを非常に簡単に達成できます

次のようになります。

  import scalaz.Scalaz._
  Map1 |+| Map2
10
Nimrod007

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]]インスタンスをスコープ内で取得し、その結合関数を使用している場合。

10
David Castillo

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)
3
x4444

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")))として連結します。簡潔にするために、m2implicitlySeqに変換されてm1.toSeqのタイプに適応しますが、m2.toSeqを使用して明示的にすることもできます。

  • 最初のタプル部分(_._1)に基づくgroups要素(group Mapのグループ部分)

  • mapsは、値を2番目のタプル部分(_._2)にグループ化します(グループのマップ部分Map

3
Xavier Guihot

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
1
Alexandr

元のマップをいじりたくない場合は、次のようなことをすることができます

val target = map1.clone()
val source = map2.clone()
source.foreach(e => target += e._1 -> e._2)
1
smishra
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の両方の上限であるタイプです。

1
Alx

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