按键合并地图
假设我有两个映射:
val a = Map(1 -> "one", 2 -> "two", 3 -> "three")
val b = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
我想按键合并这些映射,应用一些函数来收集值(在这种特殊情况下,我想将它们收集到一个序列中,给出:
val c = Map(1 -> Seq("one", "un"), 2 -> Seq("two", "deux"), 3 -> Seq("three", "trois"))
感觉应该有一种很好的、惯用的方式做这个。
Say I have two maps:
val a = Map(1 -> "one", 2 -> "two", 3 -> "three")
val b = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
I want to merge these maps by key, applying some function to collect the values (in this particular case I want to collect them into a seq, giving:
val c = Map(1 -> Seq("one", "un"), 2 -> Seq("two", "deux"), 3 -> Seq("three", "trois"))
It feels like there should be a nice, idiomatic way of doing this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
scala.collection.immutable.IntMap
有一个intersectionWith
方法,它可以精确地执行您想要的操作(我相信):这为您提供了
IntMap(1 -> List(one, un), 2 -> List(two, deux) ), 3 -> 列表(三,三))
。请注意,它正确地忽略了仅出现在a
中的键。附带说明:我经常发现自己想要来自 Scala 中的 Haskell
Data.Map
。我认为没有任何原则性的理由表明它们应该只在IntMap
上可用,而不是在基本collection.Map
特征中可用。scala.collection.immutable.IntMap
has anintersectionWith
method that does precisely what you want (I believe):This gives you
IntMap(1 -> List(one, un), 2 -> List(two, deux), 3 -> List(three, trois))
. Note that it correctly ignores the key that only occurs ina
.As a side note: I've often found myself wanting the
unionWith
,intersectionWith
, etc. functions from Haskell'sData.Map
in Scala. I don't think there's any principled reason that they should only be available onIntMap
, instead of in the basecollection.Map
trait.Scalaz 为任何可用
Semigroup[A]
的类型A
添加了方法|+|
。如果您映射了 Map,以便每个值都是单元素序列,那么您可以非常简单地使用它:
Scalaz adds a method
|+|
for any typeA
for which aSemigroup[A]
is available.If you mapped your Maps so that each value was a single-element sequence, then you could use this quite simply:
从 Scala 2.13 开始,您可以使用
groupMap
(顾名思义)相当于groupBy
后跟map
的值:这样:
将两个映射连接为元组序列 (
List((1, "one"), (2, "two"), (3, "third"))
)。为了简洁起见,map2
隐式转换为Seq
以与map1.toSeq
的类型对齐 - 但您可以选择使用map2.toSeq
使其显式化。group
元素基于其第一个元组部分 (_._1
)(groupMap 的组部分)map 将值分组到其第二个元组部分(
_._2
) (映射组Map的一部分)Starting
Scala 2.13
, you can usegroupMap
which (as its name suggests) is an equivalent of agroupBy
followed bymap
on values:This:
Concatenates the two maps as a sequence of tuples (
List((1, "one"), (2, "two"), (3, "three"))
). For conciseness,map2
is implicitly converted toSeq
to align withmap1.toSeq
's type - but you could choose to make it explicit by usingmap2.toSeq
.group
s elements based on their first tuple part (_._1
) (group part of groupMap)map
s grouped values to their second tuple part (_._2
) (map part of groupMap)在寻找其他解决方案之前,这是我的第一个方法:
为了避免碰巧只存在于 a 或 b 中的元素,过滤器很方便:
需要 Flatten,因为 b.get (x._1) 返回一个 Option。为了使扁平化工作,第一个元素也必须是一个选项,所以我们不能在这里只使用 x._2 。
对于序列,它也有效:
Here is my first approach before looking for the other solutions:
To avoid elements which happen to exist only in a or b, a filter is handy:
Flatten is needed, because b.get (x._1) returns an Option. To make flatten work, the first element has to be an option too, so we can't just use x._2 here.
For sequences, it works too:
所以我对这两种解决方案都不太满意(我想构建一种新类型,所以半群感觉不太合适,而且 Infinity 的解决方案似乎相当复杂),所以我暂时采用了这种解决方案。我很高兴看到它得到改进:
我希望当两个映射中都不存在键时不返回任何内容(这与其他解决方案不同),但是指定此方法的方法会很好。
So I wasn't quite happy with either solution (I want to build a new type, so semigroup doesn't really feel appropriate, and Infinity's solution seemed quite complex), so I've gone with this for the moment. I'd be happy to see it improved:
I wanted the behaviour of returning nothing when a key wasn't present in either map (which differs from other solutions), but a way of specifying this would be nice.