Scala:合并地图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20047080/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala: Merge map
提问by Robinho
How can I merge maps like below:
如何合并如下地图:
Map1 = Map(1 -> Class1(1), 2 -> Class1(2))
Map2 = Map(2 -> Class2(1), 3 -> Class2(2))
After merged.
合并后。
Merged = Map( 1 -> List(Class1(1)), 2 -> List(Class1(2), Class2(1)), 3 -> Class2(2))
Can be List, Set or any other collection who has size attribute.
可以是 List、Set 或任何其他具有 size 属性的集合。
回答by drexin
Using the standard lib, you can do it as follows:
使用标准库,您可以按如下方式进行:
// 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))
回答by tiran
This is the simplest implementation i could come up with,
这是我能想到的最简单的实现,
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))
回答by senia
You could use scalaz:
你可以使用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))
You could use Set(_)instead of List(_)to get Sets as values in Map.
您可以使用Set(_)而不是List(_)将Sets 作为Map.
See Semigroupin scalaz cheat sheet(or in learning scalaz) for details about |+|operator.
见半群在scalaz小抄(或学习scalaz)有关详细信息|+|运营商。
For Int|+|works as +, for List- as ++, for Mapit applies |+|to values of same keys.
ForInt|+|用作+, for List-as ++,因为Map它适用|+|于相同键的值。
回答by David Castillo
One clean way to do it, with cats:
一种干净的方法,用猫:
import cats.implicits._
Map(1 -> "Hello").combine(Map(2 -> "Goodbye"))
//Map(2 -> Goodbye, 1 -> Hello)
It's important to note that both maps have to be of the same type (in this case, Map[Int, String]).
重要的是要注意两个地图必须是相同的类型(在本例中为Map[Int, String])。
Long explanation:
长解释:
combineisn't really a member of Map. By importing cats.implicits you're bringing into scope cats's Map built-in monoid instances, along with some implicit classes which enable the terse syntax.
combine并不是真正的 Map 成员。通过导入cats.implicits,您将猫的Map 内置monoid 实例以及一些启用简洁语法的隐式类带入作用域。
The above is equivalent to this:
以上等价于:
Monoid[Map[Int, String]].combine(Map(1 -> "Hello"), Map(2 -> "Goodbye"))
Where we're using the Monoid "summoner" functionto get the Monoid[Map[Int, String]] instance in scope and using its combine function.
我们使用Monoid“summoner”函数来获取作用域中的 Monoid[Map[Int, String]] 实例并使用它的 combine 函数。
回答by Nimrod007
I wrote a blog post about this , check it out :
我写了一篇关于此的博客文章,请查看:
http://www.nimrodstech.com/scala-map-merge/
http://www.nimrodstech.com/scala-map-merge/
basically using scalaz semi group you can achieve this pretty easily
基本上使用 scalaz semi group 你可以很容易地做到这一点
would look something like :
看起来像:
import scalaz.Scalaz._
Map1 |+| Map2
回答by Xavier Guihot
Starting Scala 2.13, another solution only based on the standard library consists in using groupMapwhich (as its name suggests) is an equivalent of a groupByfollowed by mapValues:
开始Scala 2.13,另一种仅基于标准库的解决方案在于使用groupMapwhich(顾名思义)相当于 agroupBy后跟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"))
This:
这:
Concatenates the two maps as a sequence of tuples (
List((1,"a"), (2,"b"), (2,"c"), (3,"d"))). For conciseness,m2is implicitlyconverted toSeqto adapt to the type ofm1.toSeq- but you could choose to make it explicit by usingm2.toSeq.groups elements based on their first tuple part (_._1) (group part of groupMap)maps grouped values to their second tuple part (_._2) (map part of groupMap)
将两个映射连接为一个元组序列 (
List((1,"a"), (2,"b"), (2,"c"), (3,"d")))。为了简洁,m2被隐式转换为Seq适应类型m1.toSeq-但你可以选择,使其明确使用m2.toSeq。groups 元素基于它们的第一个元组部分 (_._1) (组Map 的组部分)maps 将值分组到它们的第二个元组部分 (_._2) (组Map 的映射部分)
回答by x4444
You can use foldLeft to merge two Maps of the same type
您可以使用 foldLeft 来合并两个相同类型的 Map
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))
Example:
例子:
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)
回答by Alx
m2.foldLeft(m1.mapValues{List[CommonType](_)}) { case (acc, (k, v)) =>
acc.updated(k, acc.getOrElse(k, List.empty) :+ v)
}
As noted by jwvh, List type should be specified explicitly if Class1 is not upper type bound for Class2. CommonType is a type which is upper bound for both Class1 and Class2.
正如 jwvh 所指出的,如果 Class1 不是 Class2 的类型上限,则应明确指定 List 类型。CommonType 是 Class1 和 Class2 的上限类型。
回答by smishra
If you don't want to mess around with original maps you could do something like following
如果您不想弄乱原始地图,则可以执行以下操作
val target = map1.clone()
val source = map2.clone()
source.foreach(e => target += e._1 -> e._2)
回答by Luciano
left.keys map { k => k -> List(left(k),right(k)) } toMap
Is concise and will work, assuming your two maps are leftand right. Not sure about efficiency.
假设您的两个地图是left和,则简洁且有效right。不清楚效率。
But your question is a bit ambiguous, for two reasons. You don't specify
但是你的问题有点模棱两可,有两个原因。你没有指定
- The subtyping relationship between the values (i.e.
class1,class2), - What happens if the maps have different keys
- 值之间的子类型关系(即
class1,class2), - 如果地图有不同的键会发生什么
For the first case, consider the following example:
对于第一种情况,请考虑以下示例:
val left = Map("foo" ->1, "bar" ->2)
val right = Map("bar" -> 'a', "foo" -> 'b')
Which results in
这导致
res0: Map[String,List[Int]] = Map(foo -> List(1, 98), bar -> List(2, 97))
Notice how the Chars have been converted to Ints, because of the scala type hierarchy. More generally, if in your example class1and class2are not related, you would get back a List[Any]; this is probably not what you wanted.
请注意Chars 是如何转换为Ints 的,因为 scala 类型层次结构。更一般地说,如果在您的示例中class1和class2不相关,您会得到一个List[Any]; 这可能不是您想要的。
You can work around this by dropping the Listconstructor from my answer; this will return Tuples which preserve the type:
您可以通过List从我的答案中删除构造函数来解决此问题;这将返回Tuple保留类型的 s:
res0: Map[String,(Int, Char)] = Map(foo -> (1,b), bar -> (2,a))
The second problem is what happens when you have maps that don't have the same keys. This will result in a key not foundexception. Put in another way, are you doing a left, right, or inner join of the two maps? You can disambiguate the type of join by switching to right.keysor right.keySet ++ left.keySetfor right/inner joins respectively. The later will work around the missing key problem, but maybe that's not what you want i.e. maybe you want a left or right join instead. In that case you can consider using the withDefaultmethod of Mapto ensure every key returns a value, e.g. None, but this needs a bit more work.
第二个问题是当你的地图没有相同的键时会发生什么。这将导致key not found异常。换句话说,你是在做两个地图的左连接、右连接还是内连接?您可以通过分别切换到right.keys或right.keySet ++ left.keySet右/内连接来消除连接类型的歧义。后者将解决丢失的关键问题,但也许这不是您想要的,即您可能想要左连接或右连接。在这种情况下,您可以考虑使用 的withDefault方法Map来确保每个键都返回一个值,例如None,但这需要更多的工作。

