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)

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



56

Hier ist eine andere Alternative:

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

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

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.


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

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

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.