Tuple Listesini haritaya dönüştür (ve yinelenen anahtarla başa çıkmak?)


91

Yinelenen anahtara sahip bir listeyi [("a","b"),("c","d"),("a","f")]haritaya dönüştürmenin güzel bir yolunu düşünüyordum ("a" -> ["b", "f"], "c" -> ["d"]). Normalde (python'da), liste üzerinde boş bir harita ve for-loop oluşturur ve yinelenen anahtarı kontrol ederdim. Ama burada daha ölçülü ve akıllıca bir çözüm arıyorum.

btw, burada kullandığım gerçek anahtar / değer türü (Int, Node)ve bir haritaya dönüştürmek istiyorum(Int -> NodeSeq)

Yanıtlar:


79

Gruplayın ve ardından projelendirin:

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

Katlamayı kullanmanın daha ölçekleyici yolu, oradaki gibi ( map fadımı atla ).


125

Yineleme beklemeyen veya varsayılan yineleme işleme politikasına uymayan Google çalışanları için :

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

2.12 itibariyle, varsayılan politika şu şekildedir:

Yinelenen anahtarların üzerine daha sonraki anahtarlar yazılacaktır: Bu sırasız bir koleksiyonsa, ortaya çıkan haritada hangi anahtarın olduğu tanımsızdır.


57

İşte başka bir alternatif:

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

Bu bize bir Map[String, SeqView[String,Seq[_]]]... bu kasıtlı mı?
Luigi Plinge

1
@LuigiPlinge A SeqView[String,Seq[_]]aynı zamanda bir Seq[String]. Yine de geriye dönüp baktığımda, bunun değerli olduğunu düşünmüyorum, bu yüzden view. mapValuesyine de değerler hakkında bir görüş yapacak.
Daniel C. Sobral

Bu, benim durumum için işi mükemmel bir şekilde yaptı (kurs ödevi): lazy val dictionaryByOccurrences: Map [Occurrences, List [Word]] = {val pair = for (curWord <- dictionary) verim {val curWordOccurrences = wordOccurrences (curWord) (curWordOccurrences, curWord)} pairs.groupBy ( ._1) .mapValues ​​( .map (_._ 2))}
JasonG

mapValues ​​yeni bir harita değil, bir harita görünümünü döndürür scala-lang.org/api/current/index.html#scala.collection.Map
Max Heiber

1
Muhtemelen istiyorum x.groupBy(_._1).mapValues(_.map(_._2)).map(identity)çünkü mapValuesifade her kullanıldığında yeniden hesaplanacak. Konulara
Jeffrey Aguilera

20

Yinelemeleri önemseyen Google çalışanları için:

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

Başlangıç ​​olarak Scala 2.13, koleksiyonların çoğu , (adından da anlaşılacağı gibi) a'nın eşdeğeri (daha verimli) olan groupMap yöntemiyle sağlanır ve groupByardından mapValues:

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

Bu:

  • grouptuple'ların ilk bölümünü temel alan öğeler ( Grup Haritasının grup bölümü )

  • maps ikinci tuple bölümünü alarak değerleri grupladı (grup Haritasının harita bölümü )

Bu bir eşdeğerdir list.groupBy(_._1).mapValues(_.map(_._2))ancak Listedeki bir geçişte gerçekleştirilir .


4

Burada, bir demet listesini yinelenen anahtarları işleyen bir haritaya dönüştürmenin daha Scala deyimsel bir yolu var. Bir kıvrım kullanmak istiyorsunuz.

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
Neden bunun burada sağlanan groupBy-mapValue çözümlerinden daha Scala tarzı olduğunu düşünüyorsunuz?
Make42

@ om-nom-nom ifadesi "Fold'u kullanmanın daha ölçekleyici yolu, oradaki gibi (harita f adımını atlayın)."
cevaris

Mantıklı bir tartışma bekliyordum ;-). Ne om-nom-nom ne de bağlantılı makale sorum için kanıt sağlamadı. (Yoksa kaçırdım mı?)
Make42

1
@ Make42 Tüm monoidler tekli olduğundan ve yasaya göre monoidler katlanabilir olduğundan, bununla başa çıkmanın daha fp bir yolu. Fp'de nesneler ve olaylar monad olarak modellenir ve tüm monad'ler groupBy'yi uygulamaz.
kurum

4

Aşağıda birkaç çözüm bulabilirsiniz. (GroupBy, FoldLeft, Aggregate, Spark)

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

GroupBy varyasyonu

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

Sol varyasyonunu katlayın

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

Toplam Varyasyon - Sola katlamaya benzer

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
)

Kıvılcım Değişimi - Büyük veri kümeleri için (RDD'ye ve RDD'den Düz Haritaya Dönüştürme)

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

Bunu deneyebilirsin

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.