Aprendendo Scala atualmente e necessário inverter um mapa para fazer algumas pesquisas de valor invertido-> chave. Eu estava procurando uma maneira simples de fazer isso, mas só encontrei:
(Map() ++ origMap.map(kvp=>(kvp._2->kvp._1)))
Alguém tem uma abordagem mais elegante?
scala
scala-collections
AlexeyMK
fonte
fonte
Map(1 -> "A", 2 -> "B", 3 -> "B").map(_.swap)
resulta emMap(A -> 1, B -> 3)
Matematicamente, o mapeamento pode não ser invertível (injetivo), por exemplo, de
Map[A,B]
, você não pode obterMap[B,A]
, mas simMap[B,Set[A]]
, porque pode haver chaves diferentes associadas aos mesmos valores. Então, se você estiver interessado em conhecer todas as chaves, aqui está o código:fonte
.map(_._1)
seria mais legível apenas.keys
Set
s em vez deList
s como antes..mapValues
porque ele retorna uma visualização. Ocasionalmente, é isso que você deseja, mas se você não tomar cuidado, isso pode consumir muita memória e CPU. Para forçá-lo em um mapa, você pode fazerm.groupBy(_._2).mapVaues(_.keys).map(identity)
ou pode substituir a chamada de.mapValues(_.keys)
por.map { case (k, v) => k -> v.keys }
.Você pode evitar as coisas ._1 enquanto itera de algumas maneiras.
Aqui está uma maneira. Isso usa uma função parcial que cobre o único caso que importa para o mapa:
Aqui está outra maneira:
A iteração do mapa chama uma função com uma tupla de dois elementos, e a função anônima deseja dois parâmetros. Function.tupled faz a tradução.
fonte
Eu vim aqui procurando uma maneira de inverter um Mapa do tipo Mapa [A, Seq [B]] para Mapa [B, Seq [A]], onde cada B no novo mapa está associado a cada A no mapa antigo para em que o B estava contido na sequência associada de A.
Por exemplo,
Map(1 -> Seq("a", "b"), 2-> Seq("b", "c"))
seria invertido para
Map("a" -> Seq(1), "b" -> Seq(1, 2), "c" -> Seq(2))
Esta é minha solução:
onde oldMap é do tipo
Map[A, Seq[B]]
e newMap é do tipoMap[B, Seq[A]]
Os foldLefts aninhados me fazem estremecer um pouco, mas esta é a maneira mais direta que encontrei de realizar esse tipo de inversão. Alguém tem uma solução mais limpa?
fonte
Map[A, Seq[B]]
paraMap[B, Seq[A]]
onde seus trasnforms soluçãoMap[A, Seq[B]]
paraMap[Seq[B], Seq[A]]
.a.toSeq.flatMap { case (a, b) => b.map(_ -> a) }.groupBy(_._2).mapValues(_.map(_._1))
OK, então esta é uma pergunta muito antiga com muitas respostas boas, mas eu construí o
Map
inversor definitivo e definitivo, canivete suíço, e este é o lugar para postá-lo.Na verdade, são dois inversores. Um para elementos de valor individuais ...
... e outro, bastante semelhante, para coleções de valores.
uso:
Eu preferiria ter os dois métodos na mesma classe implícita, mas quanto mais tempo eu gastava examinando isso, mais problemático parecia.
fonte
Você pode inverter um mapa usando:
O problema com essa abordagem é que se seus valores, que agora se tornaram as chaves hash em seu mapa, não forem exclusivos, você descartará os valores duplicados. Ilustrar:
Para evitar isso, você pode primeiro converter seu mapa em uma lista de tuplas e, em seguida, inverter, para não eliminar nenhum valor duplicado:
fonte
Em scala REPL:
Observe que os valores duplicados serão substituídos pela última adição ao mapa:
fonte
Começando
Scala 2.13
, para trocar chaves / valores sem perder as chaves associadas aos mesmos valores, podemos usarMap
o novo método groupMap , que (como o próprio nome sugere) é equivalente a aegroupBy
amap
ping sobre itens agrupados.Este:
group
elementos s com base em sua segunda parte da tupla (_._2
) (parte do grupo do mapa do grupo )map
s itens agrupados pegando sua primeira parte da tupla (_._1
) (parte do mapa do grupo Mapa )Isso pode ser visto como uma versão de uma passagem do
map.groupBy(_._2).mapValues(_.map(_._1))
.fonte
Map[K, C[V]]
emMap[V, C[K]]
.Inverso é um nome melhor para esta operação do que reverso (como em "inverso de uma função matemática")
Costumo fazer essa transformação inversa não apenas em mapas, mas em outras coleções (incluindo Seq). Acho melhor não limitar a definição da minha operação inversa a mapas um-para-um. Aqui está a definição com a qual trabalho para mapas (sugira melhorias para minha implementação).
Se for um mapa um-para-um, você acaba com listas singleton que podem ser trivialmente testadas e transformadas em um Mapa [B, A] ao invés de Mapa [B, Lista [A]].
fonte
Podemos tentar usar esta
foldLeft
função que cuidará das colisões e inverterá o mapa em uma travessia única.fonte