Risposte:
Supponendo che i valori siano unici, funziona:
(Map() ++ origMap.map(_.swap))
Su Scala 2.8, invece, è più semplice:
origMap.map(_.swap)
Essere in grado di farlo è parte del motivo per cui Scala 2.8 ha una nuova libreria di collezioni.
Matematicamente, la mappatura potrebbe non essere invertibile (iniettiva), ad es. Da Map[A,B]
, non puoi ottenere Map[B,A]
, ma piuttosto ottieni Map[B,Set[A]]
, perché potrebbero esserci chiavi diverse associate agli stessi valori. Quindi, se sei interessato a conoscere tutte le chiavi, ecco il codice:
scala> val m = Map(1 -> "a", 2 -> "b", 4 -> "b")
scala> m.groupBy(_._2).mapValues(_.keys)
res0: Map[String,Iterable[Int]] = Map(b -> Set(2, 4), a -> Set(1))
.map(_._1)
sarebbe più leggibile come solo.keys
Set
s invece di List
s come prima.
.mapValues
perché restituisce una vista. Di tanto in tanto, questo è quello che vuoi, ma se non stai attento può consumare molta memoria e CPU. Per forzarlo in una mappa, puoi farlo m.groupBy(_._2).mapVaues(_.keys).map(identity)
o potresti sostituire la chiamata a .mapValues(_.keys)
con .map { case (k, v) => k -> v.keys }
.
Puoi evitare le cose ._1 durante l'iterazione in diversi modi.
Ecco un modo. Questo utilizza una funzione parziale che copre l'unico caso importante per la mappa:
Map() ++ (origMap map {case (k,v) => (v,k)})
Ecco un altro modo:
import Function.tupled
Map() ++ (origMap map tupled {(k,v) => (v,k)})
L'iterazione della mappa chiama una funzione con una tupla a due elementi e la funzione anonima vuole due parametri. Function.tupled effettua la traduzione.
Sono venuto qui cercando un modo per invertire una mappa di tipo Map [A, Seq [B]] in Map [B, Seq [A]], dove ogni B nella nuova mappa è associato a ogni A nella vecchia mappa per quale la B era contenuta nella sequenza associata di A.
Ad esempio,
Map(1 -> Seq("a", "b"), 2-> Seq("b", "c"))
invertirebbe in
Map("a" -> Seq(1), "b" -> Seq(1, 2), "c" -> Seq(2))
Ecco la mia soluzione:
val newMap = oldMap.foldLeft(Map[B, Seq[A]]().withDefaultValue(Seq())) {
case (m, (a, bs)) => bs.foldLeft(m)((map, b) => map.updated(b, m(b) :+ a))
}
dove oldMap è di tipo Map[A, Seq[B]]
e newMap è di tipoMap[B, Seq[A]]
I foldLeft nidificati mi fanno rabbrividire un po ', ma questo è il modo più semplice che ho trovato per realizzare questo tipo di inversione. Qualcuno ha una soluzione più pulita?
Map[A, Seq[B]]
da Map[B, Seq[A]]
dove i vostri trasnforms soluzione Map[A, Seq[B]]
a Map[Seq[B], Seq[A]]
.
a.toSeq.flatMap { case (a, b) => b.map(_ -> a) }.groupBy(_._2).mapValues(_.map(_._1))
OK, questa è una domanda molto vecchia con molte buone risposte, ma ho costruito il coltellino svizzero definitivo, universale, Map
inverter e questo è il posto giusto per pubblicarlo.
In realtà sono due inverter. Uno per i singoli elementi di valore ...
//from Map[K,V] to Map[V,Set[K]], traverse the input only once
implicit class MapInverterA[K,V](m :Map[K,V]) {
def invert :Map[V,Set[K]] =
m.foldLeft(Map.empty[V, Set[K]]) {
case (acc,(k, v)) => acc + (v -> (acc.getOrElse(v,Set()) + k))
}
}
... e un altro, abbastanza simile, per le collezioni di valore.
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.Builder
import scala.language.higherKinds
//from Map[K,C[V]] to Map[V,C[K]], traverse the input only once
implicit class MapInverterB[K,V,C[_]](m :Map[K,C[V]]
)(implicit ev :C[V] => TraversableOnce[V]) {
def invert(implicit bf :CanBuildFrom[Nothing,K,C[K]]) :Map[V,C[K]] =
m.foldLeft(Map.empty[V, Builder[K,C[K]]]) {
case (acc, (k, vs)) =>
vs.foldLeft(acc) {
case (a, v) => a + (v -> (a.getOrElse(v,bf()) += k))
}
}.mapValues(_.result())
}
utilizzo:
Map(2 -> Array('g','h'), 5 -> Array('g','y')).invert
//res0: Map(g -> Array(2, 5), h -> Array(2), y -> Array(5))
Map('q' -> 1.1F, 'b' -> 2.1F, 'c' -> 1.1F, 'g' -> 3F).invert
//res1: Map(1.1 -> Set(q, c), 2.1 -> Set(b), 3.0 -> Set(g))
Map(9 -> "this", 8 -> "that", 3 -> "thus", 2 -> "thus").invert
//res2: Map(this -> Set(9), that -> Set(8), thus -> Set(3, 2))
Map(1L -> Iterator(3,2), 5L -> Iterator(7,8,3)).invert
//res3: Map(3 -> Iterator(1, 5), 2 -> Iterator(1), 7 -> Iterator(5), 8 -> Iterator(5))
Map.empty[Unit,Boolean].invert
//res4: Map[Boolean,Set[Unit]] = Map()
Preferirei avere entrambi i metodi nella stessa classe implicita, ma più tempo passavo a esaminarlo più appariva problematico.
Puoi invertire una mappa usando:
val i = origMap.map({case(k, v) => v -> k})
Il problema con questo approccio è che se i tuoi valori, che ora sono diventati le chiavi hash nella tua mappa, non sono univoci, lascerai cadere i valori duplicati. Illustrare:
scala> val m = Map("a" -> 1, "b" -> 2, "c" -> 3, "d" -> 1)
m: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3, d -> 1)
// Notice that 1 -> a is not in our inverted map
scala> val i = m.map({ case(k , v) => v -> k})
i: scala.collection.immutable.Map[Int,String] = Map(1 -> d, 2 -> b, 3 -> c)
Per evitare ciò, puoi prima convertire la tua mappa in un elenco di tuple, quindi invertire, in modo da non rilasciare valori duplicati:
scala> val i = m.toList.map({ case(k , v) => v -> k})
i: List[(Int, String)] = List((1,a), (2,b), (3,c), (1,d))
In scala REPL:
scala> val m = Map(1 -> "one", 2 -> "two")
m: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two)
scala> val reversedM = m map { case (k, v) => (v, k) }
reversedM: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)
Tieni presente che i valori duplicati verranno sovrascritti dall'ultima aggiunta alla mappa:
scala> val m = Map(1 -> "one", 2 -> "two", 3 -> "one")
m: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two, 3 -> one)
scala> val reversedM = m map { case (k, v) => (v, k) }
reversedM: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 3, two -> 2)
Iniziando Scala 2.13
, per scambiare chiave / valori senza perdere le chiavi associate agli stessi valori, possiamo usare Map
il nuovo metodo groupMap , che (come suggerisce il nome) è l'equivalente di un groupBy
e un map
ping su elementi raggruppati.
Map(1 -> "a", 2 -> "b", 4 -> "b").groupMap(_._2)(_._1)
// Map("b" -> List(2, 4), "a" -> List(1))
Questo:
group
s elementi basati sulla loro seconda parte tupla ( _._2
) (parte del gruppo della mappa del gruppo )
map
s raggruppa gli elementi prendendo la loro prima tupla part ( _._1
) (map part of group Map )
Questo può essere visto come una versione one-pass di map.groupBy(_._2).mapValues(_.map(_._1))
.
Map[K, C[V]]
in Map[V, C[K]]
.
Inverse è un nome migliore per questa operazione che reverse (come in "inverso di una funzione matematica")
Faccio spesso questa trasformazione inversa non solo sulle mappe ma su altre raccolte (incluso Seq). Trovo sia meglio non limitare la definizione della mia operazione inversa alle mappe uno-a-uno. Ecco la definizione con cui opero per le mappe (suggerisci miglioramenti alla mia implementazione).
def invertMap[A,B]( m: Map[A,B] ) : Map[B,List[A]] = {
val k = ( ( m values ) toList ) distinct
val v = k map { e => ( ( m keys ) toList ) filter { x => m(x) == e } }
( k zip v ) toMap
}
Se si tratta di una mappa uno-a-uno, si ottengono elenchi singleton che possono essere banalmente testati e trasformati in una mappa [B, A] invece che in una mappa [B, List [A]].
Possiamo provare a utilizzare questa foldLeft
funzione che si prenderà cura delle collisioni e invertirà la mappa in un unico attraversamento.
scala> def invertMap[A, B](inputMap: Map[A, B]): Map[B, List[A]] = {
| inputMap.foldLeft(Map[B, List[A]]()) {
| case (mapAccumulator, (value, key)) =>
| if (mapAccumulator.contains(key)) {
| mapAccumulator.updated(key, mapAccumulator(key) :+ value)
| } else {
| mapAccumulator.updated(key, List(value))
| }
| }
| }
invertMap: [A, B](inputMap: Map[A,B])Map[B,List[A]]
scala> val map = Map(1 -> 2, 2 -> 2, 3 -> 3, 4 -> 3, 5 -> 5)
map: scala.collection.immutable.Map[Int,Int] = Map(5 -> 5, 1 -> 2, 2 -> 2, 3 -> 3, 4 -> 3)
scala> invertMap(map)
res0: Map[Int,List[Int]] = Map(5 -> List(5), 2 -> List(1, 2), 3 -> List(3, 4))
scala> val map = Map("A" -> "A", "B" -> "A", "C" -> "C", "D" -> "C", "E" -> "E")
map: scala.collection.immutable.Map[String,String] = Map(E -> E, A -> A, B -> A, C -> C, D -> C)
scala> invertMap(map)
res1: Map[String,List[String]] = Map(E -> List(E), A -> List(A, B), C -> List(C, D))
Map(1 -> "A", 2 -> "B", 3 -> "B").map(_.swap)
comportaMap(A -> 1, B -> 3)