venta studio programacion móviles fabricacion escala distribuidora desarrollo curso coleccion carros bogota autos aplicaciones scala map scala-collections

studio - ¿La mejor manera de Scala de convertir una colección en un mapa por clave?



fabricacion de carros a escala (11)

Además de la solución de @James Iry, también es posible lograr esto utilizando un doblez. Sospecho que esta solución es ligeramente más rápida que el método de tupla (se crean menos objetos basura):

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

Si tengo una colección c de tipo T y hay una propiedad p en T (de tipo P , por ejemplo), ¿cuál es la mejor manera de hacer un mapa por extraer clave ?

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

Una forma es la siguiente:

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

Pero ahora necesito un mapa mutable . ¿Hay una forma mejor de hacer esto para que esté en 1 línea y termine con un mapa inmutable ? (Obviamente, podría convertir lo anterior en una utilidad de biblioteca simple, como lo haría en Java, pero sospecho que en Scala no es necesario)


Esto funciona para mí:

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

El mapa debe ser mutable y el mapa debe ser devuelto, ya que agregarlo a un mapa mutable no devuelve un mapa.


Esto se puede implementar inmutablemente y con un solo recorrido cruzando la colección de la siguiente manera.

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

La solución funciona porque al agregar a un mapa inmutable, se devuelve un nuevo mapa inmutable con la entrada adicional y este valor sirve como acumulador a través de la operación de plegado.

La compensación aquí es la simplicidad del código versus su eficiencia. Por lo tanto, para colecciones grandes, este enfoque puede ser más adecuado que usar 2 implementaciones transversales como la aplicación de map y toMap .


Lo que estás tratando de lograr es un poco indefinido.
¿Qué sucede si dos o más elementos en c comparten el mismo p ? ¿Qué elemento se asignará a esa p en el mapa?

La forma más precisa de ver esto es generar un mapa entre p y todos los elementos c que lo tienen:

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

Esto podría lograrse fácilmente con groupBy :

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

Si aún desea el mapa original, puede, por ejemplo, asignar p a la primera t que lo tiene:

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


Otra solución (podría no funcionar para todos los tipos)

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

esto evita la creación de la lista de intermediarios, más información aquí: Scala 2.8 breakOut


Por lo que vale, aquí hay dos maneras inútiles de hacerlo:

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


Probablemente esta no sea la forma más eficiente de convertir una lista en un mapa, pero hace que el código de llamada sea más legible. Utilicé conversiones implícitas para agregar un método mapBy a la Lista:

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

Ejemplo del código de llamada:

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

Tenga en cuenta que debido a la conversión implícita, el código llamante necesita importar las implicitConversions de Scala.


Puede construir un Mapa con un número variable de tuplas. Así que utiliza el método de mapa en la colección para convertirlo en una colección de tuplas y luego usa el truco: _ * para convertir el resultado en un argumento variable.

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)


Puedes usar

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

pero ten en cuenta que esto necesita 2 travesías.


use map () en la colección seguida de toMapa

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


c map (_.getP) zip c

Funciona bien y es muy intuitivo