说我有两张地图:
val a = Map(1 -> "one", 2 -> "two", 3 -> "three")
val b = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
我想通过键合并这些地图,应用一些函数来收集值(在这种特殊情况下,我想将它们收集到一个seq中,给出:
val c = Map(1 -> Seq("one", "un"), 2->Seq("two", "deux"), 3->Seq("three", "trois"))
感觉应该有一个很好的,惯用的方式来做这个 - 任何建议?如果解决方案涉及scalaz,我很高兴。
答案 0 :(得分:20)
scala.collection.immutable.IntMap
有一个intersectionWith
方法可以完全按照您的意愿行事(我相信):
import scala.collection.immutable.IntMap
val a = IntMap(1 -> "one", 2 -> "two", 3 -> "three", 4 -> "four")
val b = IntMap(1 -> "un", 2 -> "deux", 3 -> "trois")
val merged = a.intersectionWith(b, (_, av, bv: String) => Seq(av, bv))
这会给你IntMap(1 -> List(one, un), 2 -> List(two, deux), 3 -> List(three, trois))
。请注意,它正确地忽略了仅在a
中出现的密钥。
作为旁注:我经常发现自己想要Scala中Haskell's Data.Map
的unionWith
,intersectionWith
等功能。我认为没有任何原则性原因,它们只应在IntMap
上提供,而不是在基本collection.Map
特征中提供。
答案 1 :(得分:18)
val a = Map(1 -> "one", 2 -> "two", 3 -> "three")
val b = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
val c = a.toList ++ b.toList
val d = c.groupBy(_._1).map{case(k, v) => k -> v.map(_._2).toSeq}
//res0: scala.collection.immutable.Map[Int,Seq[java.lang.String]] =
//Map((2,List(two, deux)), (1,List(one, un), (3,List(three, trois)))
答案 2 :(得分:14)
Scalaz为|+|
可用的任何类型A
添加了Semigroup[A]
方法。
如果您映射地图以使每个值都是单个元素序列,那么您可以非常简单地使用它:
scala> a.mapValues(Seq(_)) |+| b.mapValues(Seq(_))
res3: scala.collection.immutable.Map[Int,Seq[java.lang.String]] = Map(1 -> List(one, un), 2 -> List(two, deux), 3 -> List(three, trois))
答案 3 :(得分:2)
所以我对这两种解决方案都不太满意(我想建立一个新类型,所以半群并不真正感觉合适,而且Infinity的解决方案看起来相当复杂),所以我暂时不知道这个。我很高兴看到它有所改善:
def merge[A,B,C](a : Map[A,B], b : Map[A,B])(c : (B,B) => C) = {
for (
key <- (a.keySet ++ b.keySet);
aval <- a.get(key); bval <- b.get(key)
) yield c(aval, bval)
}
merge(a,b){Seq(_,_)}
我想要在任何一个地图中都没有密钥时返回任何内容的行为(这与其他解决方案不同),但是指定这个的方法会很好。
答案 4 :(得分:1)
在寻找其他解决方案之前,这是我的第一个方法:
for (x <- a) yield
x._1 -> Seq (a.get (x._1), b.get (x._1)).flatten
为了避免碰巧只存在于a或b中的元素,过滤器很方便:
(for (x <- a) yield
x._1 -> Seq (a.get (x._1), b.get (x._1)).flatten).filter (_._2.size == 2)
需要展平,因为b.get(x._1)会返回一个选项。为了使展平工作,第一个元素也必须是一个选项,所以我们不能在这里使用x._2。
对于序列,它也有效:
scala> val b = Map (1 -> Seq(1, 11, 111), 2 -> Seq(2, 22), 3 -> Seq(33, 333), 5 -> Seq(55, 5, 5555))
b: scala.collection.immutable.Map[Int,Seq[Int]] = Map(1 -> List(1, 11, 111), 2 -> List(2, 22), 3 -> List(33, 333), 5 -> List(55, 5, 5555))
scala> val a = Map (1 -> Seq(1, 101), 2 -> Seq(2, 212, 222), 3 -> Seq (3, 3443), 4 -> (44, 4, 41214))
a: scala.collection.immutable.Map[Int,ScalaObject with Equals] = Map(1 -> List(1, 101), 2 -> List(2, 212, 222), 3 -> List(3, 3443), 4 -> (44,4,41214))
scala> (for (x <- a) yield x._1 -> Seq (a.get (x._1), b.get (x._1)).flatten).filter (_._2.size == 2)
res85: scala.collection.immutable.Map[Int,Seq[ScalaObject with Equals]] = Map(1 -> List(List(1, 101), List(1, 11, 111)), 2 -> List(List(2, 212, 222), List(2, 22)), 3 -> List(List(3, 3443), List(33, 333)))
答案 5 :(得分:1)
val fr = Map(1 -> "one", 2 -> "two", 3 -> "three")
val en = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
def innerJoin[K, A, B](m1: Map[K, A], m2: Map[K, B]): Map[K, (A, B)] = {
m1.flatMap{ case (k, a) =>
m2.get(k).map(b => Map((k, (a, b)))).getOrElse(Map.empty[K, (A, B)])
}
}
innerJoin(fr, en) // Map(1 -> ("one", "un"), 2 -> ("two", "deux"), 3 -> ("three", "trois")): Map[Int, (String, String)]
答案 6 :(得分:0)
从Scala 2.13
开始,您可以使用groupMap
,该https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/mbstowcs-s-mbstowcs-s-l?view=vs-2019相当于groupBy
,后跟map
的值:
// val map1 = Map(1 -> "one", 2 -> "two", 3 -> "three")
// val map2 = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
(map1.toSeq ++ map2).groupMap(_._1)(_._2)
// Map(1 -> List("one", "un"), 2 -> List("two", "deux"), 3 -> List("three", "trois"))
此:
将两个映射串联为一个元组(List((1, "one"), (2, "two"), (3, "three"))
)序列。为简洁起见,将map2
隐式地转换为Seq
以与map1.toSeq
的类型对齐-但您可以选择使用{{1}使其明确}。
map2.toSeq
的元素基于它们的第一个元组部分(group
)(组地图的组部分)
_._1
的值分为第二个元组部分(map
)(组 Map 的映射部分)
答案 7 :(得分:0)
Field