scala - son - tuplas append
Convierte una lista de tupla en un mapa(¿y lidias con una clave duplicada?) (7)
Estaba pensando en una buena forma de convertir una Lista de tuplas con la clave duplicada [("a","b"),("c","d"),("a","f")]
en el mapa ("a" -> ["b", "f"], "c" -> ["d"])
. Normalmente (en python), crearía un mapa vacío y for-loop sobre la lista y verificaría la clave duplicada. Pero estoy buscando algo más scala-ish y una solución inteligente aquí.
Por cierto, el tipo real de clave-valor que uso aquí es (Int, Node)
y quiero convertirme en un mapa de (Int -> NodeSeq)
Aquí hay otra alternativa:
x.groupBy(_._1).mapValues(_.map(_._2))
Aquí hay una forma más idiomática de Scala para convertir una lista de tuplas en un mapa que maneja claves duplicadas. Quieres usar un doblez
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))
Grupo y luego proyecto:
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))
Una forma más escalada de usar fold, en la forma como there (omita map f
step).
Para los Googlers que no esperan duplicados o que están de acuerdo con la política de manejo de duplicados predeterminada :
List("a" -> 1, "b" -> 2).toMap
// Result: Map(a -> 1, c -> 2)
A partir del 2.12, la política predeterminada dice:
Las claves posteriores sobrescribirán las claves duplicadas: si se trata de una colección desordenada, la clave que está en el mapa resultante no está definida.
Para los Googlers que se preocupan por los duplicados:
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"))
Puedes intentar esto
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)
A continuación puede encontrar algunas soluciones. (GroupBy, FoldLeft, Aggregate, Spark)
val list: List[(String, String)] = List(("a","b"),("c","d"),("a","f"))
Variación GroupBy
list.groupBy(_._1).map(v => (v._1, v._2.map(_._2)))
Doble variación a la izquierda
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))
}
})
Variación agregada: similar a doblar a la izquierda
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
)
Variación de chispa - Para grandes conjuntos de datos (Conversión a un RDD y a un Mapa simple desde 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