我在想一种将重复键[("a","b"),("c","d"),("a","f")]
的元组列表转换为映射("a" -> ["b", "f"], "c" -> ["d"])
的好方法。通常(在python中),我会在列表上创建一个空映射和for循环,并检查重复键。但我在这里寻找更多的scala-ish和聪明的解决方案。
btw,我在这里使用的实际键值类型是(Int, Node)
,我想变成(Int -> NodeSeq)
答案 0 :(得分:111)
对于那些不期望重复或对default duplicate handling policy感到满意的Google员工:
List("a" -> 1, "b" -> 2).toMap
// Result: Map(a -> 1, c -> 2)
从2.12开始,默认策略为:
以后的密钥会覆盖重复的密钥:如果这是一个无序的集合,那么结果映射中的哪个密钥是未定义的。
答案 1 :(得分:72)
分组然后投射:
scala> val x = List("a" -> "b", "c" -> "d", "a" -> "f")
//x: List[(java.lang.String, java.lang.String)] = List((a,b), (c,d), (a,f))
scala> x.groupBy(_._1).map { case (k,v) => (k,v.map(_._2))}
//res1: scala.collection.immutable.Map[java.lang.String,List[java.lang.String]] = Map(c -> List(d), a -> List(b, f))
使用折叠的更加谨慎的方式,就像there一样(跳过map f
步骤)。
答案 2 :(得分:52)
这是另一种选择:
x.groupBy(_._1).mapValues(_.map(_._2))
答案 3 :(得分:18)
对于那些关心重复的Google员工:
implicit class Pairs[A, B](p: List[(A, B)]) {
def toMultiMap: Map[A, List[B]] = p.groupBy(_._1).mapValues(_.map(_._2))
}
> List("a" -> "b", "a" -> "c", "d" -> "e").toMultiMap
> Map("a" -> List("b", "c"), "d" -> List("e"))
答案 4 :(得分:4)
以下是将元组列表转换为处理重复键的映射的更多Scala惯用方法。你想使用折叠。
val x = List("a" -> "b", "c" -> "d", "a" -> "f")
x.foldLeft(Map.empty[String, Seq[String]]) { case (acc, (k, v)) =>
acc.updated(k, acc.getOrElse(k, Seq.empty[String]) ++ Seq(v))
}
res0: scala.collection.immutable.Map[String,Seq[String]] = Map(a -> List(b, f), c -> List(d))
答案 5 :(得分:4)
从Scala 2.13
开始,大多数集合都提供了groupMap方法,该方法(顾名思义)与groupBy
后跟mapValues
等效(效率更高) :
List("a" -> "b", "c" -> "d", "a" -> "f").groupMap(_._1)(_._2)
// Map[String,List[String]] = Map(a -> List(b, f), c -> List(d))
此:
group
的元素基于元组的第一部分(组地图的组部分)
map
的值通过其第二元组部分(组地图的地图部分)
这等效于list.groupBy(_._1).mapValues(_.map(_._2))
,但performed in one pass在列表中。
答案 6 :(得分:3)
您可以在下面找到一些解决方案。 (GroupBy,FoldLeft,Aggregate,Spark)
val list: List[(String, String)] = List(("a","b"),("c","d"),("a","f"))
GroupBy变体
list.groupBy(_._1).map(v => (v._1, v._2.map(_._2)))
折叠左变异
list.foldLeft[Map[String, List[String]]](Map())((acc, value) => {
acc.get(value._1).fold(acc ++ Map(value._1 -> List(value._2))){ v =>
acc ++ Map(value._1 -> (value._2 :: v))
}
})
汇总变化 - 类似于左侧折叠
list.aggregate[Map[String, List[String]]](Map())(
(acc, value) => acc.get(value._1).fold(acc ++ Map(value._1 ->
List(value._2))){ v =>
acc ++ Map(value._1 -> (value._2 :: v))
},
(l, r) => l ++ r
)
Spark变异 - 适用于大数据集(从RDD转换为RDD和平面地图)
import org.apache.spark.rdd._
import org.apache.spark.{SparkContext, SparkConf}
val conf: SparkConf = new
SparkConf().setAppName("Spark").setMaster("local")
val sc: SparkContext = new SparkContext (conf)
// This gives you a rdd of the same result
val rdd: RDD[(String, List[String])] = sc.parallelize(list).combineByKey(
(value: String) => List(value),
(acc: List[String], value) => value :: acc,
(accLeft: List[String], accRight: List[String]) => accLeft ::: accRight
)
// To convert this RDD back to a Map[(String, List[String])] you can do the following
rdd.collect().toMap
答案 7 :(得分:1)
你可以试试这个
scala> val b = new Array[Int](3)
// b: Array[Int] = Array(0, 0, 0)
scala> val c = b.map(x => (x -> x * 2))
// c: Array[(Int, Int)] = Array((1,2), (2,4), (3,6))
scala> val d = Map(c : _*)
// d: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 2 -> 4, 3 -> 6)