2010-05-27 13 views
26

Ho un Seq contenenti oggetti di una classe che assomiglia a questo:Come convertire un Seq [A] in una mappa [Int, A] usando il valore di A come chiave nella mappa?

class A (val key: Int, ...) 

Ora voglio convertire questo Seq ad un Map, utilizzando il valore key di ogni oggetto come chiave, e l'oggetto stesso come il valore. Quindi:

val seq: Seq[A] = ... 
val map: Map[Int, A] = ... // How to convert seq to map? 

Come posso farlo in modo efficiente ed elegante in Scala 2.8?

+0

Fuori curiosità a qualcuno capita di sapere perché questo non è incluso nella libreria delle collezioni Scala? – tksfz

risposta

24

Mappa su Seq e produrre una sequenza di tuple. Quindi utilizzare quelle tuple per creare un Map. Funziona in tutte le versioni di Scala.

val map = Map(seq map { a => a.key -> a }: _*) 
+2

L'uso di 'breakOut' come mostra Seth Tisue in un'altra risposta può renderlo più efficiente evitando di creare una sequenza temporanea di tuple. – Jesper

44

Dal 2.8 Scala ha avuto .toMap, quindi:

val map = seq.map(a => a.key -> a).toMap

o se siete Gung Ho di evitare la costruzione di una sequenza intermedia di tuple:

val map: Map[Int, A] = seq.map(a => a.key -> a)(collection.breakOut)

+0

Grazie. Ho già visto la cosa 'breakout 'ma non so ancora cosa sia. È ora di imparare qualcosa di nuovo. – Jesper

+3

Trovato una buona spiegazione di 'breakOut' qui: http: // stackoverflow.it/questions/1715681/scala-2-8-breakout/1716558 # 1716558 – Jesper

+0

Perché val map = Seq (1,2,3) .map (a => a -> a) (collection.breakOut) è effettivamente di tipo Vector? mappa: scala.collection.immutable.IndexedSeq [(Int, Int)] = Vector ((1,1), (2,2), (3,3)) – simou

5

Ancora una variazione 2.8, per buona misura, anche efficiente:

scala> case class A(key: Int, x: Int) 
defined class A 

scala> val l = List(A(1, 2), A(1, 3), A(2, 1)) 
l: List[A] = List(A(1,2), A(1,3), A(2,1)) 

scala> val m: Map[Int, A] = (l, l).zipped.map(_.key -> _)(collection.breakOut) 
m: Map[Int,A] = Map((1,A(1,3)), (2,A(2,1))) 

Nota che se hai chiavi duplicate, ne scartane alcune durante la creazione della mappa! Si potrebbe utilizzare groupBy per creare una mappa in cui ogni valore è una sequenza:

scala> l.groupBy(_.key) 
res1: scala.collection.Map[Int,List[A]] = Map((1,List(A(1,2), A(1,3))), (2,List(A(2,1)))) 
1

Come Scala sa per convertire una tupla di due a una mappa, si dovrebbe prima desidera convertire il vostro SEQ per una tupla e poi mappare così (non importa se si tratta di int, nel nostro caso di stringa, string):

L'algoritmo generale è questo:

  1. Per ogni elemento Seq
  2. Item -> Tuple (chiave, valore)
  3. Per ogni tupla (chiave, valore)
  4. Aggregato alla mappa (chiave, valore)

O per riassumere:

Fase 1: Seq -> tupla di due

fase 2: tupla di due -> Mappa

Esempio:

case class MyData(key: String, value: String) // One item in seq to be converted to a map entry. 

// Our sequence, simply a seq of MyData 
val myDataSeq = Seq(MyData("key1", "value1"), MyData("key2", "value2"), MyData("key3", "value3")) // List((key1,value1), (key2,value2), (key3,value3)) 

// Step 1: Convert seq to tuple 
val myDataSeqAsTuple = myDataSeq.map(myData => (myData.key, myData.value)) // List((key1,value1), (key2,value2), (key3,value3)) 

// Step 2: Convert tuple of two to map. 
val myDataFromTupleToMap = myDataSeqAsTuple.toMap // Map(key1 -> value1, key2 -> value2, key3 -> value3) 
Problemi correlati