2010-05-27 10 views
26

que tienen un Seq que contienen objetos de una clase que tiene este aspecto:¿Cómo convertir una Seq [A] a un Mapa [Int, A] usando un valor de A como la clave en el mapa?

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

Ahora quiero convertir este Seq a un Map, utilizando el valor key de cada objeto como la clave, y el objeto a sí mismo como el valor. Entonces:

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

¿Cómo puedo hacerlo de manera eficiente y elegante en Scala 2.8?

+0

Fuera de curiosidades ¿Alguien sabe por qué esto no está incluido en la biblioteca de colecciones de Scala? – tksfz

Respuesta

24

Mapa sobre su Seq y produzca una secuencia de tuplas. Luego use esas tuplas para crear un Map. Funciona en todas las versiones de Scala.

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

Usar 'breakOut' como Seth Tisue muestra en otra respuesta puede hacerlo más eficiente evitando crear una secuencia temporal de tuplas. – Jesper

44

Desde 2.8 Scala ha tenido .toMap, por lo que:

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

o si está súper entusiasmado acerca de evitar la construcción de una secuencia intermedia de tuplas:

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

+0

Gracias. He visto el tema 'breakOut' antes, pero todavía no sé qué es. Es hora de aprender algo nuevo otra vez. – Jesper

+3

Encontré una buena explicación de 'breakOut' aquí: http: // stackoverflow.com/preguntas/1715681/scala-2-8-ruptura/1716558 # 1716558 – Jesper

+0

Por qué es que un mapa val = Seq (1,2,3) .map (a => a -> a) (collection.breakOut) es en realidad de tipo Vector? mapa: scala.collection.immutable.IndexedSeq [(Int, Int)] = Vector ((1,1), (2,2), (3,3)) – simou

5

Una variación más 2.8, para una buena medida, también eficiente:

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

Tenga en cuenta que si tiene claves duplicadas, las descartará durante la creación del mapa. Usted podría utilizar groupBy para crear un mapa donde cada valor es una secuencia:

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

Como Scala sabe convertir una tupla de dos a un mapa, primero desea convertir sus siguientes a una tupla y luego al mapa por lo que (no importa si es int, en nuestro caso la cadena, cadena):

El algoritmo general es la siguiente:

  1. Para cada elemento en la Seq
  2. artículo -> tupla (clave, valor)
  3. Para cada tupla (clave, valor)
  4. agregada al mapa (clave, valor)

O para resumir:

Paso 1: Sec -> tupla de dos

paso 2: tupla de dos -> Mapa

Ejemplo:

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