将元组列表转换为映射(并处理重复键?)

Tg.*_*Tg. 86 scala map

我正在考虑一种将带有重复键的元组列表转换[("a","b"),("c","d"),("a","f")]为映射的好方法("a" -> ["b", "f"], "c" -> ["d"]).通常(在python中),我会在列表上创建一个空映射和for循环,并检查重复键.但我在这里寻找更多scala-ish和聪明的解决方案.

顺便说一句,我在这里使用的实际键值类型是(Int, Node),我想变成一张地图(Int -> NodeSeq)

Cor*_*ein 117

对于不期望重复的Google员工,或者对于默认的重复处理政策没有意义的Google员工:

List("a" -> 1, "b" -> 2).toMap
// Result: Map(a -> 1, c -> 2)
Run Code Online (Sandbox Code Playgroud)

从2.12开始,默认策略为:

稍后的键将覆盖重复的键:如果这是一个无序的集合,则生成的映射中的哪个键是未定义的.


om-*_*nom 74

小组然后项目:

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))
Run Code Online (Sandbox Code Playgroud)

更像是使用折叠的方式,就像那里一样(跳过map f步骤).


Dan*_*ral 54

这是另一种选择:

x.groupBy(_._1).mapValues(_.map(_._2))
Run Code Online (Sandbox Code Playgroud)


pat*_*rit 19

对于那些关心重复项的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")) 
Run Code Online (Sandbox Code Playgroud)


Xav*_*hot 7

Starting Scala 2.13,大多数集合都提供有groupMap方法,顾名思义,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))
Run Code Online (Sandbox Code Playgroud)

这个:

  • group的元素基于元组的第一部分( Map的组部分)

  • map通过取它们的第二元组部S分组的值(映射组的一部分地图

这等效于list.groupBy(_._1).mapValues(_.map(_._2))但通过列表一次执行


小智 6

您可以在下面找到一些解决方案。(GroupBy、FoldLeft、聚合、Spark)

val list: List[(String, String)] = List(("a","b"),("c","d"),("a","f"))
Run Code Online (Sandbox Code Playgroud)

按变化分组

list.groupBy(_._1).map(v => (v._1, v._2.map(_._2)))
Run Code Online (Sandbox Code Playgroud)

向左折叠变化

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))
  }
})
Run Code Online (Sandbox Code Playgroud)

聚合变化 - 类似于左折叠

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
)
Run Code Online (Sandbox Code Playgroud)

Spark Variation - 对于大数据集(转换为 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
Run Code Online (Sandbox Code Playgroud)


cev*_*ris 5

下面是一种更惯用的 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))
Run Code Online (Sandbox Code Playgroud)