Я думал о хорошем способе преобразовать список кортежей с повторяющимся ключом [("a","b"),("c","d"),("a","f")]
в карту ("a" -> ["b", "f"], "c" -> ["d"])
. Обычно (в python) я бы создавал пустую карту и цикл for по списку и проверял наличие повторяющегося ключа. Но я ищу здесь что-то более масштабное и умное.
кстати, реальный тип пары "ключ-значение", которую я здесь использую, - это, (Int, Node)
и я хочу превратить ее в карту(Int -> NodeSeq)
Map[String, SeqView[String,Seq[_]]]
... это намеренно?SeqView[String,Seq[_]]
- это тожеSeq[String]
. Оглядываясь назад, я не думаю, что это стоит того, поэтому я удалилview
.mapValues
в любом случае будет просматривать значения.x.groupBy(_._1).mapValues(_.map(_._2)).map(identity)
потому чтоmapValues
выражение будет пересчитываться каждый раз при его использовании. См. Issues.scala-lang.org/browse/SI-7005Для гуглеров, которые заботятся о дубликатах:
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"))
источник
Начиная с
Scala 2.13
большинства коллекций, предоставляется метод 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
s элементов на основе первой части кортежей (групповая часть групповой карты)map
s сгруппированные значения, взяв их вторую часть кортежа (часть карты группы Map )Это эквивалентно выполнению,
list.groupBy(_._1).mapValues(_.map(_._2))
но выполняется за один проход по списку.источник
Вот более идиоматический способ 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))
источник
Ниже вы можете найти несколько решений. (GroupBy, FoldLeft, Aggregate, Spark)
val list: List[(String, String)] = List(("a","b"),("c","d"),("a","f"))
Группировать по вариации
Сложить левый вариант
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 )
Spark Variation - для больших наборов данных (преобразование в 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
источник
Вы можете попробовать это
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)
источник