我正在考虑一种将具有重复键的元组列表转换[("a","b"),("c","d"),("a","f")]
为map 的好方法("a" -> ["b", "f"], "c" -> ["d"])
。通常(在python中),我将创建一个空映射并在列表上进行for循环,并检查重复的键。但是我在这里寻找一种更轻松,更聪明的解决方案。
顺便说一句,我在这里使用的键值的实际类型是(Int, Node)
,我想变成一个映射(Int -> NodeSeq)
Answers:
分组然后进行项目:
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))
使用折痕的更平整的方式,就像那里一样(跳过map f
步骤)。
这是另一种选择:
x.groupBy(_._1).mapValues(_.map(_._2))
SeqView[String,Seq[_]]
也是一个Seq[String]
。事后看来,我认为这不值得,因此我删除了view
。mapValues
无论如何都会对这些值进行查看。
x.groupBy(_._1).mapValues(_.map(_._2)).map(identity)
因为mapValues
每次使用表达式时都会重新计算该表达式。参见issues.scala-lang.org/browse/SI-7005
Starting Scala 2.13
,大多数集合都提供有groupMap方法,顾名思义,groupMap方法等效于(更有效)a 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的组部分)
map
通过取它们的第二元组部S分组的值(映射组的一部分地图)
这等效于list.groupBy(_._1).mapValues(_.map(_._2))
但通过列表一次执行。
这是一种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))
您可以在下面找到一些解决方案。(GroupBy,FoldLeft,Aggregate,Spark)
val list: List[(String, String)] = List(("a","b"),("c","d"),("a","f"))
通过分组
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
)
火花变化-适用于大数据集(从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
Map[String, SeqView[String,Seq[_]]]
……这是故意的吗?