Scala, bir koleksiyonu anahtar haritaya dönüştürmenin en iyi yolu mu?


165

Bir ctür koleksiyonum varsa Tve (tür , örneğin) püzerinde bir özellik varsa, bir eşleme anahtarı ile yapmanın en iyi yolu nedir?TP

val c: Collection[T]
val m: Map[P, T]

Bunun bir yolu şudur:

m = new HashMap[P, T]
c foreach { t => m add (t.getP, t) }

Ama şimdi değişebilir bir haritaya ihtiyacım var . O 1 doğrultusunda ve ben bir ile bitirmek böylece bunu yapmanın daha iyi bir yolu var mı değişmez Haritası? (Açıkçası yukarıdakileri Java'da olduğu gibi basit bir kütüphane yardımcı programına dönüştürebilirim, ancak Scala'da gerek olmadığından şüpheleniyorum)

Yanıtlar:


232

Kullanabilirsiniz

c map (t => t.getP -> t) toMap

ancak bunun 2 geçişe ihtiyaç duyduğunu unutmayın.


8
Hala benim trac benim önerilerini tercih Traversable[K].mapTo( K => V)ve Traversable[V].mapBy( V => K)daha iyiydi!
oxbow_lakes

7
Bunun ikinci dereceden bir işlem olduğunu unutmayın, ancak burada verilen diğer birçok varyant için de aynı şey geçerlidir. Scala.collection.mutable.MapBuilder vb. Kaynak koduna baktığımda, bana her bir grup için, grubun eklendiği yeni bir değişmez harita oluşturulduğu anlaşılıyor.
jcsahnwaldt Yeniden Monica Monica

30
Makinemde 500.000 öğe içeren bir liste için, bu Scala kodu düz Java yaklaşımından yaklaşık 20 kat daha yavaştır (uygun boyutta HashMap oluşturun, listeye döngü yapın, öğeleri haritaya yerleştirin). 5.000 element için Scala yaklaşık 8 kat daha yavaştır. Scala'da yazılan döngü yaklaşımı, toMap değişkeninden kabaca 3 kat daha hızlıdır, ancak yine de Java'dan 2 ila 7 kat daha yavaştır.
jcsahnwaldt Yeniden Monica Monica

8
Test kaynaklarını SO topluluğuna bildirir misiniz? Teşekkürler.
user573215

8
Ara toplama oluşturulmasını önlemek için cile değiştirin c.iterator.
ghik

21

Değişken sayıda tuple içeren bir Harita oluşturabilirsiniz. Bu yüzden koleksiyondaki map yöntemini kullanarak bir tuples koleksiyonuna dönüştürün ve ardından sonucu değişken bir argümana dönüştürmek için: _ * hile kullanın.

scala> val list = List("this", "maps", "string", "to", "length") map {s => (s, s.length)}
list: List[(java.lang.String, Int)] = List((this,4), (maps,4), (string,6), (to,2), (length,6))

scala> val list = List("this", "is", "a", "bunch", "of", "strings")
list: List[java.lang.String] = List(this, is, a, bunch, of, strings)

scala> val string2Length = Map(list map {s => (s, s.length)} : _*)
string2Length: scala.collection.immutable.Map[java.lang.String,Int] = Map(strings -> 7, of -> 2, bunch -> 5, a -> 1, is -> 2, this -> 4)

5
Scala hakkında 2 haftadır okuyorum ve örnekler üzerinde çalışıyorum ve bu ": _ *" gösterimini hiç görmemiştim! Yardımınız için çok teşekkürler
oxbow_lakes

Sadece kayıt için, neden bu _ ile bir dizi olduğunu kesin olarak ihtiyacımız olduğunu merak ediyorum . harita hala dönüştürmek tuple listesi buraya dön. Peki neden _ ? Yani işe yarıyor ama burada tip
yazısını

1
Bu diğer yöntemlerden daha mı verimli?
Jus12

16

@James Iry'nin çözümüne ek olarak, bunu bir katlama kullanarak da yapmak mümkündür. Bu çözüm tuple yönteminden biraz daha hızlı olduğundan şüpheleniyorum (daha az çöp nesnesi oluşturulur):

val list = List("this", "maps", "string", "to", "length")
val map = list.foldLeft(Map[String, Int]()) { (m, s) => m(s) = s.length }

Bunu deneyeceğim (işe yaradığından eminim :-). "(M, s) => m (s) = s.length" işleviyle neler oluyor? Bir toplamı ve bir işlevi "_ + _" ile tipik foldLeft örnek gördük; bu çok daha kafa karıştırıcı! Fonksiyon, zaten gerçekten alamadım bir demet (m, s) olduğunu varsayıyor gibi görünüyor
oxbow_lakes

2
Dostum, Scala o zaman garipti!
missingfaktor

8
@Daniel Kodunuzu deniyorum ancak şu hatayı görüyorum: "değer güncellemesi scala.collection.immutable.Map [String, Int] 'nin bir üyesi değil". Lütfen kodunuzu bu kodun nasıl çalıştığını açıklayın?
mr.boyfox

1
iş için görünmüyor. benim için "Uygulama parametreleri almaz"
jayunit100

7
Immutable versiyon: list.foldLeft(Map[String,Int]()) { (m,s) => m + (s -> s.length) }. Eğer tuple inşa etmek virgül kullanmak istiyorsanız Not parantez fazladan bir çift gerektiğini: ((s, s.length)).
Kelvin

11

Bu, derlemeden ve katlama yoluyla aşağıdaki gibi katlanarak tek bir geçişle uygulanabilir.

val map = c.foldLeft(Map[P, T]()) { (m, t) => m + (t.getP -> t) }

Çözüm, değişmez bir Haritaya eklemek, ek giriş ile yeni bir değişmez Harita döndürdüğü ve bu değerin katlama işlemi boyunca akümülatör görevi görmesi nedeniyle çalışır.

Buradaki takas, kodun verimliliğine karşı sadeliğidir. Bu nedenle, büyük koleksiyonlar için, bu yaklaşım mapve gibi 2 çapraz uygulama kullanmaktan daha uygun olabilir toMap.


8

Başka bir çözüm (tüm türler için çalışmayabilir)

import scala.collection.breakOut
val m:Map[P, T] = c.map(t => (t.getP, t))(breakOut)

bu, aracı listesinin oluşturulmasını önler, daha fazla bilgi burada: Scala 2.8 breakOut


7

Ulaşmaya çalıştığınız şey biraz tanımsız.
Ya iki veya daha fazla öğe cpaylaşıyorsa p? pHaritada hangi öğe eşlenecek ?

Buna bakmanın en doğru yolu, sahip olduğu ptüm cöğeler arasında bir harita sunmaktır :

val m: Map[P, Collection[T]]

Bu, groupBy ile kolayca gerçekleştirilebilir :

val m: Map[P, Collection[T]] = c.groupBy(t => t.p)

Yine de orijinal haritayı istiyorsanız, örneğin, ona sahip polan ilk haritayı eşleyebilirsiniz t:

val m: Map[P, T] = c.groupBy(t => t.p) map { case (p, ts) =>  p -> ts.head }

1
Bu konuda kullanışlı bir değişiklik collectyerine kullanmaktır map. Ör: c.group(t => t.p) collect { case (Some(p), ts) => p -> ts.head }. Bu şekilde, bir Seçenek [_] tuşuna bastığınızda haritaları düzleştir gibi şeyler yapabilirsiniz.
16:54

@healsjnr Elbette, bu herhangi bir harita için söylenebilir. Yine de buradaki temel mesele değil.
Eyal Roth

1
.mapValues(_.head)Harita yerine kullanabilirsiniz .
lex82

2

Bu, bir listeyi eşleştirmek için en etkili yol değildir, ancak arama kodunu daha okunabilir hale getirir. Listeye bir mapBy yöntemi eklemek için örtük dönüşümler kullandım :

implicit def list2ListWithMapBy[T](list: List[T]): ListWithMapBy[T] = {
  new ListWithMapBy(list)
}

class ListWithMapBy[V](list: List[V]){
  def mapBy[K](keyFunc: V => K) = {
    list.map(a => keyFunc(a) -> a).toMap
  }
}

Telefon kodu örneği:

val list = List("A", "AA", "AAA")
list.mapBy(_.length)                  //Map(1 -> A, 2 -> AA, 3 -> AAA)

Örtük dönüşüm nedeniyle, arayan kodunun scala'nın implicitConversions dosyasını içe aktarması gerektiğini unutmayın.


2
c map (_.getP) zip c

İyi çalışıyor ve çok sezgisel


8
Lütfen daha fazla ayrıntı ekleyin.
Syeda Zunaira

2
Üzgünüm. Ama bu, "Scala'yı bir Koleksiyonu anahtar haritaya dönüştürmenin en iyi yolu mu?" Ben Lings gibi.
Jörg Bächtiger

1
Ben hiçbir açıklama yapmadı mı?
shinzou

1
bu iki liste oluşturur ve canahtar (tür) olarak öğeleri kullanarak bir "harita" birleştirir . Sonuçta elde edilen koleksiyon bir scala olmadığından, Mapancak başka bir liste / tuples'lerden oluşabileceği için "harita" yı not edin ... ancak etki OP'nin amacı için aynıdır, basitliği indirmezdim, ancak foldLeftçözüm kadar verimli değil , ne de "bir koleksiyona anahtar haritaya dönüştürülüyor" sorusunun gerçek cevabı
Dexter Legaspi


1

Değeri için, işte bunu yapmanın iki anlamsız yolu:

scala> case class Foo(bar: Int)
defined class Foo

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> val c = Vector(Foo(9), Foo(11))
c: scala.collection.immutable.Vector[Foo] = Vector(Foo(9), Foo(11))

scala> c.map(((_: Foo).bar) &&& identity).toMap
res30: scala.collection.immutable.Map[Int,Foo] = Map(9 -> Foo(9), 11 -> Foo(11))

scala> c.map(((_: Foo).bar) >>= (Pair.apply[Int, Foo] _).curried).toMap
res31: scala.collection.immutable.Map[Int,Foo] = Map(9 -> Foo(9), 11 -> Foo(11))

Ayrıca, fwiw, bu ikisi Haskell'de şöyle görünecekti: Map.fromList $ map (bar &&& id) c, Map.fromList $ map (bar >>= (,)) c.
missingfaktor

-1

Bu benim için çalışıyor:

val personsMap = persons.foldLeft(scala.collection.mutable.Map[Int, PersonDTO]()) {
    (m, p) => m(p.id) = p; m
}

Değiştirilebilir bir Haritaya ekleme bir harita döndürmediği için Harita değiştirilebilir ve Harita geri döndürülmelidir.


1
Aslında, aşağıdaki gibi değişmez bir şekilde uygulanabilir: val personsMap = persons.foldLeft(Map[Int, PersonDTO]()) { (m, p) => m + (p.id -> p) }Harita, yukarıda kanıtlandığı gibi değişmez olabilir, çünkü değişmez bir Haritaya eklemek, ek giriş ile yeni bir değişmez Harita döndürür. Bu değer katlama işlemi boyunca akümülatör görevi görür.
RamV13

-2

Koleksiyonda map () kullanın ve ardından toMap kullanın

val map = list.map(e => (e, e.length)).toMap

3
Bunun 7 yıl önce gönderilen ve kabul edilen cevaptan farkı nedir?
jwvh

-4

Json String'ten (json dosyasını okuma) scala Map'e dönüştürüyorsanız

import spray.json._
import DefaultJsonProtocol._

val jsonStr = Source.fromFile(jsonFilePath).mkString
val jsonDoc=jsonStr.parseJson
val map_doc=jsonDoc.convertTo[Map[String, JsValue]]

// Get a Map key value
val key_value=map_doc.get("key").get.convertTo[String]

// If nested json, re-map it.
val key_map=map_doc.get("nested_key").get.convertTo[Map[String, JsValue]]
println("Nested Value " + key_map.get("key").get)

Bu 11 yaşındaki soru JSON hakkında hiçbir şey sormuyor. Cevabınız yerinde değil.
jwvh
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.