Liste der Tupel in Map konvertieren (und mit doppeltem Schlüssel umgehen?)

90

Ich dachte über eine gute Möglichkeit nach, eine Liste von Tupeln mit doppeltem Schlüssel [("a","b"),("c","d"),("a","f")]in eine Karte umzuwandeln ("a" -> ["b", "f"], "c" -> ["d"]). Normalerweise (in Python) würde ich eine leere Map erstellen und die Liste for-loopen und nach doppelten Schlüsseln suchen. Aber ich bin hier auf der Suche nach einer etwas schäbigeren und klügeren Lösung.

Übrigens ist der tatsächliche Typ des Schlüsselwerts, den ich hier verwende, (Int, Node)und ich möchte mich in eine Karte von verwandeln(Int -> NodeSeq)

Tg.
quelle

Antworten:

78

Gruppe und dann Projekt:

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

Skaliertere Art, Fold zu verwenden, wie dort ( map fSchritt überspringen ).

OM Nom Nom
quelle
124

Für Googler, die keine Duplikate erwarten oder mit der Standardrichtlinie für die Behandlung von Duplikaten einverstanden sind :

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

Ab 2.12 lautet die Standardrichtlinie:

Doppelte Schlüssel werden durch spätere Schlüssel überschrieben: Wenn es sich um eine ungeordnete Sammlung handelt, ist der Schlüssel in der resultierenden Zuordnung undefiniert.

Cory Klein
quelle
56

Hier ist eine andere Alternative:

x.groupBy(_._1).mapValues(_.map(_._2))
Daniel C. Sobral
quelle
Dies gibt uns eine Map[String, SeqView[String,Seq[_]]]... ist das beabsichtigt?
Luigi Plinge
1
@LuigiPlinge A SeqView[String,Seq[_]]ist auch ein Seq[String]. Im Nachhinein denke ich nicht, dass sich das lohnt, also habe ich das entfernt view. mapValueswird trotzdem eine Ansicht über die Werte machen.
Daniel C. Sobral
Dies hat den Job für meinen Fall perfekt gemacht (Coursera-Hausaufgaben): Lazy Val DictionaryByOccurrences: Map [Vorkommen, Liste [Word]] = {Val Pair = = (CurWord <- Wörterbuch) Ausbeute {Val CurWordOccurrences = WordOccurrences (CurWord) (CurWordOccurrences, curWord)} pairs.groupBy ( ._1) .mapValues ​​( .map (_._ 2))}
JasonG
mapValues ​​gibt eine Ansicht einer Karte zurück, keine neue Karte scala-lang.org/api/current/index.html#scala.collection.Map
Max Heiber
1
Wahrscheinlich wollen, x.groupBy(_._1).mapValues(_.map(_._2)).map(identity)weil der mapValuesAusdruck jedes Mal neu berechnet wird, wenn er verwendet wird. Siehe Issues.scala-lang.org/browse/SI-7005
Jeffrey Aguilera
20

Für Googler, die sich für Duplikate interessieren:

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")) 
Pathikrit
quelle
12

Beginnend Scala 2.13werden die meisten Sammlungen mit der groupMap- Methode bereitgestellt , die (wie der Name schon sagt) einer äquivalenten (effizienteren) Methode entspricht, groupBygefolgt von mapValues:

List("a" -> "b", "c" -> "d", "a" -> "f").groupMap(_._1)(_._2)
// Map[String,List[String]] = Map(a -> List(b, f), c -> List(d))

Dies:

  • groups Elemente basierend auf dem ersten Teil von Tupeln (Gruppenteil von Gruppenzuordnung )

  • mapgruppierte Werte s mit ihrem zweiten Tupel teilnimmt (Teil der Gruppe Karte Karte )

Dies entspricht, wird list.groupBy(_._1).mapValues(_.map(_._2))jedoch in einem Durchgang durch die Liste ausgeführt.

Xavier Guihot
quelle
4

Hier ist eine Scala-idiomatischere Möglichkeit, eine Liste von Tupeln in eine Karte zu konvertieren, die doppelte Schlüssel verarbeitet. Sie möchten eine Falte verwenden.

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))
Cevaris
quelle
1
Warum ist dies Ihrer Meinung nach mehr Scala-Stil als die hier bereitgestellten groupBy-mapValue-Lösungen?
Make42
@ om-nom-nom-Anweisung "Skaliertere Verwendung von Fold, wie dort (überspringe Karte f Schritt)."
Cevaris
Ich hatte auf ein logisches Argument gehofft ;-). Weder om-nom-nom noch der verlinkte Artikel lieferten Beweise für meine Frage. (Oder habe ich es vermisst?)
Make42
1
@ Make42 Es ist eine schnellere Möglichkeit, damit umzugehen, da alle Monaden Monoide sind und Monoide per Gesetz faltbar sind. In fp werden Objekte und Ereignisse als Monaden modelliert, und nicht alle Monaden implementieren groupBy.
Soote
4

Nachfolgend finden Sie einige Lösungen. (GroupBy, FoldLeft, Aggregate, Spark)

val list: List[(String, String)] = List(("a","b"),("c","d"),("a","f"))

GroupBy-Variation

list.groupBy(_._1).map(v => (v._1, v._2.map(_._2)))

Variation links falten

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

Aggregierte Variation - Ähnlich wie links falten

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
)

Spark Variation - Für große Datenmengen (Konvertierung in eine RDD und in eine einfache Karte von 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
Melcom van Eeden
quelle
2

Sie können dies versuchen

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