Aprendiendo Scala actualmente y necesitaba invertir un mapa para hacer algunas búsquedas de valores invertidos-> claves. Estaba buscando una forma sencilla de hacer esto, pero solo se me ocurrió:
(Map() ++ origMap.map(kvp=>(kvp._2->kvp._1)))
¿Alguien tiene un enfoque más elegante?
scala
scala-collections
AlexeyMK
fuente
fuente
Map(1 -> "A", 2 -> "B", 3 -> "B").map(_.swap)
resulta enMap(A -> 1, B -> 3)
Matemáticamente, el mapeo puede no ser invertible (inyectivo), por ejemplo, de
Map[A,B]
, no se puede obtenerMap[B,A]
, sino que se obtieneMap[B,Set[A]]
, porque puede haber diferentes claves asociadas con los mismos valores. Entonces, si está interesado en conocer todas las claves, aquí está el código:fuente
.map(_._1)
sería más legible como justo.keys
Set
s en lugar deList
s como antes..mapValues
porque devuelve una vista. Ocasionalmente, esto es lo que desea, pero si no tiene cuidado, puede consumir mucha memoria y CPU. Para forzarlo en un mapa, puede hacerlom.groupBy(_._2).mapVaues(_.keys).map(identity)
o puede reemplazar la llamada a.mapValues(_.keys)
por.map { case (k, v) => k -> v.keys }
.Puede evitar las cosas ._1 mientras itera de varias maneras.
He aquí una forma. Esto usa una función parcial que cubre el único caso que importa para el mapa:
He aquí otra forma:
La iteración del mapa llama a una función con una tupla de dos elementos y la función anónima quiere dos parámetros. Function.tupled realiza la traducción.
fuente
Vine aquí buscando una manera de invertir un mapa de tipo Map [A, Seq [B]] a Map [B, Seq [A]], donde cada B en el nuevo mapa está asociado con cada A en el mapa antiguo para que el B estaba contenido en la secuencia asociada de A.
Por ejemplo,
Map(1 -> Seq("a", "b"), 2-> Seq("b", "c"))
invertiría a
Map("a" -> Seq(1), "b" -> Seq(1, 2), "c" -> Seq(2))
Esta es mi solución:
donde oldMap es de tipo
Map[A, Seq[B]]
y newMap es de tipoMap[B, Seq[A]]
Los foldLefts anidados me hacen encoger un poco, pero esta es la forma más sencilla que pude encontrar para lograr este tipo de inversión. ¿Alguien tiene una solución más limpia?
fuente
Map[A, Seq[B]]
aMap[B, Seq[A]]
donde sus trasnforms soluciónMap[A, Seq[B]]
aMap[Seq[B], Seq[A]]
.a.toSeq.flatMap { case (a, b) => b.map(_ -> a) }.groupBy(_._2).mapValues(_.map(_._1))
De acuerdo, esta es una pregunta muy antigua con muchas buenas respuestas, pero he construido el
Map
inversor de navaja suiza definitivo y definitivo y este es el lugar para publicarlo.En realidad, son dos inversores. Uno para los elementos de valor individuales ...
... y otro, bastante similar, para colecciones de valor.
uso:
Preferiría tener ambos métodos en la misma clase implícita, pero cuanto más tiempo pasaba investigándolo, más problemático parecía.
fuente
Puede invertir un mapa usando:
El problema con este enfoque es que si sus valores, que ahora se han convertido en las claves hash en su mapa, no son únicos, eliminará los valores duplicados. Para ilustrar:
Para evitar esto, puede convertir su mapa en una lista de tuplas primero, luego invertir, para que no suelte ningún valor duplicado:
fuente
En scala REPL:
Tenga en cuenta que los valores duplicados se sobrescribirán con la última adición al mapa:
fuente
Comenzando
Scala 2.13
, para intercambiar claves / valores sin perder las claves asociadas a los mismos valores, podemos usarMap
el nuevo método groupMap , que (como su nombre indica) es un equivalente de aygroupBy
unmap
ping sobre elementos agrupados.Esta:
group
s elementos basados en su segunda parte de tupla (_._2
) (parte de grupo del mapa de grupo )map
s elementos agrupados mediante la adopción de su primera parte tupla (_._1
) (mapa de parte del grupo Mapa )Esto puede verse como una versión de un solo paso de
map.groupBy(_._2).mapValues(_.map(_._1))
.fuente
Map[K, C[V]]
enMap[V, C[K]]
.Inverso es un nombre mejor para esta operación que inverso (como en "inverso de una función matemática")
A menudo hago esta transformación inversa no solo en mapas sino en otras colecciones (incluida Seq). Me parece mejor no limitar la definición de mi operación inversa a mapas uno a uno. Aquí está la definición con la que opero para mapas (sugiera mejoras a mi implementación).
Si se trata de un mapa uno a uno, terminará con listas singleton que se pueden probar y transformar trivialmente en un mapa [B, A] en lugar de un mapa [B, lista [A]].
fuente
Podemos intentar usar esta
foldLeft
función que se encargará de las colisiones e invertirá el mapa en un solo recorrido.fuente