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


90

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

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

Answers:


78

分组然后进行项目:

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步骤)。


124

对于不希望重复或使用默认重复处理政策的 Google员工,请执行以下操作

List("a" -> 1, "b" -> 2).toMap
// Result: Map(a -> 1, c -> 2)

从2.12开始,默认策略为:

重复的键将被以后的键覆盖:如果这是一个无序集合,则未定义结果映射中的哪个键。


56

这是另一种选择:

x.groupBy(_._1).mapValues(_.map(_._2))

这给了我们Map[String, SeqView[String,Seq[_]]]……这是故意的吗?
路易吉·普林格

1
@LuigiPlinge A SeqView[String,Seq[_]]也是一个Seq[String]。事后看来,我认为这不值得,因此我删除了viewmapValues无论如何都会对这些值进行查看。
Daniel C. Sobral

这对我的情况(课程家庭作业)而言是完美的工作:懒惰的val词典。 curWord)} pair.groupBy(._1).mapValues( .map(_._ 2))}
JasonG

mapValues返回地图视图,而不是新地图。scala
Max Heiber

1
可能需要,x.groupBy(_._1).mapValues(_.map(_._2)).map(identity)因为mapValues每次使用表达式时都会重新计算该表达式。参见issues.scala-lang.org/browse/SI-7005
Jeffrey Aguilera

20

对于确实关心重复项的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")) 

12

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))但通过列表一次执行


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))

1
您为什么认为这比此处提供的groupBy-mapValue解决方案更具Scala风格?
Make42 '16

@ om-nom-nom语句“使用折叠的更复杂的方法,就像那里一样(跳过映射f步骤)。”
cevaris '16

我希望有一个合理的论点;-)。om-nom-nom和链接的文章都没有为我的问题提供证据。(还是我错过了?)
Make42 '16

1
@ Make42这是处理此问题的更有效的方法,因为所有monads都是monoid,而根据法律,monoid是可折叠的。在fp中,对象和事件被建模为monad,并非所有monad都会实现groupBy。
soote

4

您可以在下面找到一些解决方案。(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

2

你可以试试这个

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.