Je pensais à un bon moyen de convertir une liste de tuple avec une clé dupliquée [("a","b"),("c","d"),("a","f")]
en carte ("a" -> ["b", "f"], "c" -> ["d"])
. Normalement (en python), je créerais une carte et une boucle vides sur la liste et rechercherais une clé en double. Mais je cherche quelque chose de plus scala-ish et de solution intelligente ici.
d'ailleurs, le type actuel de valeur-clé que j'utilise ici est (Int, Node)
et je veux transformer en une carte de (Int -> NodeSeq)
Groupe puis projet:
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))
Une manière plus évolutive d'utiliser fold, à la manière de there (skip map f
étape).
Pour les utilisateurs qui n'attendent pas de doublons ou qui acceptent les doublons politique de traitement des doublons par défaut :
List("a" -> 1, "b" -> 2).toMap
// Result: Map(a -> 1, c -> 2)
À partir de la version 2.12, la stratégie par défaut est la suivante:
Les clés en double seront remplacées par des clés ultérieures: s'il s'agit d'une collection non ordonnée, la clé qui est dans la carte résultante est indéfinie.
Voici une autre alternative:
x.groupBy(_._1).mapValues(_.map(_._2))
À partir de Scala 2.13
, La plupart des collections sont fournies avec la méthode groupMap qui est (comme son nom l'indique) un équivalent (plus efficace) d'un groupBy
suivi de mapValues
:
List("a" -> "b", "c" -> "d", "a" -> "f").groupMap(_._1)(_._2)
// Map[String,List[String]] = Map(a -> List(b, f), c -> List(d))
Cette:
group
s éléments basés sur la première partie des tuples (partie du groupe du groupe Map)
map
s valeurs groupées en prenant leur deuxième partie Tuple (partie carte du groupe Carte )
C'est l'équivalent de list.groupBy(_._1).mapValues(_.map(_._2))
mais exécuté en une passe dans la liste.
Vous trouverez ci-dessous quelques solutions. (GroupBy, FoldLeft, Aggregate, Spark)
val list: List[(String, String)] = List(("a","b"),("c","d"),("a","f"))
Variation GroupBy
list.groupBy(_._1).map(v => (v._1, v._2.map(_._2)))
variation à gauche
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))
}
})
Variation globale - Semblable au pli gauche
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
)
Variation Spark - Pour les grands ensembles de données (Conversion en RDD et en Plain Map à partir de 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
Voici un moyen plus Scala idiomatique de convertir une liste de n-uplets en une carte gérant les clés dupliquées. Vous souhaitez utiliser un pli.
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))
Tu peux essayer ça
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)