Como faço para trocar chaves com valores em um dicionário?


97

Recebo um dicionário como entrada e gostaria de retornar um dicionário cujas chaves serão os valores de entrada e cujo valor serão as chaves de entrada correspondentes. Os valores são únicos.

Por exemplo, digamos que minha entrada seja:

a = dict()
a['one']=1
a['two']=2

Eu gostaria que minha saída fosse:

{1: 'one', 2: 'two'}

Para esclarecer, gostaria que meu resultado fosse o equivalente ao seguinte:

res = dict()
res[1] = 'one'
res[2] = 'two'

Alguma maneira Pythônica legal de conseguir isso?


1
Consulte stackoverflow.com/questions/1087694/… para uma pergunta idêntica que tem uma boa resposta se você estiver usando Python 3
Stephen Edmonds

@Stephen: veja a segunda resposta mais votada, é a mesma que a aceita na pergunta à qual você vinculou. A multidão preferiu a outra resposta, porém ...
Roee Adler,

4
Python não é perl, python não é ruby. A legibilidade conta. O esparso é melhor do que o denso. Diante disso, todos os métodos dessas respostas são simplesmente ruins ™; aquele em questão é o melhor caminho a seguir.
o0 '.

3
possível duplicata do mapeamento reverso / inverso
Cory

Respostas:


148

Python 2:

res = dict((v,k) for k,v in a.iteritems())

Python 3 (graças a @erik):

res = dict((v,k) for k,v in a.items())

14
Embora isso pareça estar correto, é realmente bom adicionar uma explicação de como funciona, em vez de apenas o código.
Será de

4
E se os valores não forem únicos? Então as chaves devem ser uma lista ... por exemplo: d = {'a': 3, 'b': 2, 'c': 2} {v: k para k, v in d.iteritems ()} { 2: 'b', 3: 'a'} deve ser {2: ['b', 'c'], 3: 'a'}
Hanan Shteingart

4
@HananShteingart: os valores declarados da pergunta do OP são únicos. Crie uma postagem de pergunta separada para o seu caso (e de preferência vincule-a aqui para outras pessoas).
liori

o código python2 funciona ... mas falta a compreensão da lista [e ]. a compreensão de uma lista não requer o [e ]?
Trevor Boyd Smith

@TrevorBoydSmith: isso não é compreensão de lista, é uma expressão geradora .
liori

55
new_dict = dict(zip(my_dict.values(), my_dict.keys()))

4
Os valores () e as chaves () têm a garantia de ter a mesma ordem?
Lennart Regebro

1
sim, de python.org/dev/peps/pep-3106 A especificação implica que a ordem em que os itens são retornados por .keys (), .values ​​() e .items () é a mesma (exatamente como era em Python 2.x), porque a ordem é toda derivada do iterador do dict (que é presumivelmente arbitrário, mas estável, desde que um dict não seja modificado). mas essa resposta precisa chamar my_dict duas vezes (uma para valores, uma para chaves). talvez isso não seja o ideal.
sunqiang

4
Sim, essa resposta itera duas vezes no dict. A resposta de sunqiang é preferível para um dicionário grande, pois requer apenas uma iteração.
Carl Meyer

@Carl Meyer: concordo, também, ele está usando itertools que são muito melhores para grandes conjuntos de dados. embora eu me pergunte se a chamada final de dict () também está em streaming, ou se primeiro reúne toda a lista de pares
Javier

@CarlMeyer adicionalmente para n> 1e6 (ou 1e9) o uso de memória também será muito grande ... e também diminuirá um pouco.
Trevor Boyd Smith

47

A partir do Python 2.7, incluindo 3.0+, há uma versão indiscutivelmente mais curta e legível:

>>> my_dict = {'x':1, 'y':2, 'z':3}
>>> {v: k for k, v in my_dict.items()}
{1: 'x', 2: 'y', 3: 'z'}

30
In [1]: my_dict = {'x':1, 'y':2, 'z':3}

In [2]: dict((value, key) for key, value in my_dict.iteritems())
Out[2]: {1: 'x', 2: 'y', 3: 'z'}

3
Não na pergunta original, estou apenas curioso para saber o que aconteceria se você tivesse valores duplicados no dicionário original e, em seguida, trocasse os valores-chave com este método?
Andre Miller

2
@Andre Miller: Leva a última ocorrência da chave específica: dict (((1,3), (1,2))) == {1: 2}
balpha

2
duplicatas serão sobrescritas com o último ladrão encontrado.
Christopher

2
@Andre Miller: E como d.items () retorna itens em uma ordem arbitrária, você obtém uma chave arbitrária para valores duplicados.
Formigas Aasma

Acho que pegará o último par de chave e valor encontrado. É como um ['x'] = 3. Então você define um ['x'] = 4.
riza


18

Você poderia tentar:

d={'one':1,'two':2}
d2=dict((value,key) for key,value in d.iteritems())
d2
  {'two': 2, 'one': 1}

Esteja ciente de que você não pode 'inverter' um dicionário se

  1. Mais de uma chave compartilha o mesmo valor. Por exemplo {'one':1,'two':1}. O novo dicionário só pode ter um item com chave 1.
  2. Um ou mais dos valores são inalteráveis. Por exemplo {'one':[1]}. [1]é um valor válido, mas não uma chave válida.

Veja este tópico na lista de discussão do python para uma discussão sobre o assunto.


Também +1 sobre a nota sobre como certificar-se de que os valores no dicionário original são únicos; caso contrário, você obterá sobrescritos no dicionário 'reverso' ... e isso (acabei de descobrir que custou caro) pode causar bugs complicados em seu código!
monojohnny

15

res = dict(zip(a.values(), a.keys()))


4
dict não garante que seus valores () e keys () retornarão elementos na mesma ordem. Além disso, keys (), values ​​() e zip () retornam uma lista, onde um iterador seria suficiente.
liori

19
@liori: Você está errado. O dict garante que seus valores () e keys () ESTARÃO na mesma ordem, se você não modificar o dict entre as chamadas para values ​​() e keys (), é claro. A documentação afirma que aqui: (leia a parte "Nota": docs.python.org/library/stdtypes.html#dict.items ) "If items (), keys (), values ​​(), iteritems (), iterkeys ( ) e itervalues ​​() são chamados sem modificações no dicionário, as listas corresponderão diretamente. "
nosklo

1
Ok, então estou errado ... Não verifiquei os documentos online. Obrigado por apontar isso.
liori

Você pode usar o iterador itertools.izip em vez de zip para tornar essa resposta mais eficiente.
Alasdair

E iterkeys e itervalues. Mas poderia muito bem usar iteritems ()
nosklo

15

A resposta principal atual assume que os valores são únicos, o que nem sempre é o caso. E se os valores não forem únicos? Você perderá informações! Por exemplo:

d = {'a':3, 'b': 2, 'c': 2} 
{v:k for k,v in d.iteritems()} 

retorna {2: 'b', 3: 'a'}.

A informação sobre 'c'foi completamente ignorada. Idealmente, deveria ser algo assim {2: ['b','c'], 3: ['a']}. Isso é o que a implementação inferior faz.

Python 2.x

def reverse_non_unique_mapping(d):
    dinv = {}
    for k, v in d.iteritems():
        if v in dinv:
            dinv[v].append(k)
        else:
            dinv[v] = [k]
    return dinv

Python 3.x

def reverse_non_unique_mapping(d):
    dinv = {}
    for k, v in d.items():
        if v in dinv:
            dinv[v].append(k)
        else:
            dinv[v] = [k]
    return dinv

esta deve ser a resposta correta, pois cobre um caso mais geral
Leon Rai

Obrigado por isso! Eu estava perdendo informações com as outras soluções.
Câmera de

14
new_dict = dict( (my_dict[k], k) for k in my_dict)

ou ainda melhor, mas só funciona no Python 3:

new_dict = { my_dict[k]: k for k in my_dict}

2
Na verdade, Dict Comprehensions ( PEP 274 ) funcionam com Python 2.7 também.
Arseny

10

Outra maneira de expandir a resposta de Ilya Prokin é realmente usar a reversedfunção.

dict(map(reversed, my_dict.items()))

Em essência, seu dicionário é iterado (usando .items()) onde cada item é um par chave / valor e esses itens são trocados com a reversedfunção. Quando isso é passado para o dictconstrutor, ele os transforma em pares de valor / chave que é o que você deseja.


6

Sugestão de melhoria para a resposta de Javier:

dict(zip(d.values(),d))

Em vez de d.keys()escrever apenas d, porque se você percorrer o dicionário com um iterador, ele retornará as chaves do dicionário relevante.

Ex. para este comportamento:

d = {'a':1,'b':2}
for k in d:
 k
'a'
'b'

3

Pode ser feito facilmente com a compreensão do dicionário:

{d[i]:i for i in d}

2
dict(map(lambda x: x[::-1], YourDict.items()))

.items()retorna uma lista de tuplas de (key, value). map()passa por elementos da lista e aplica lambda x:[::-1]a cada um de seus elementos (tupla) para revertê-la, de modo que cada tupla se torna (value, key)na nova lista cuspida fora do mapa. Por fim, dict()faz um ditado da nova lista.


.items () retorna uma lista de tuplas (chave, valor). map () passa por elementos da lista e se aplica lambda x:[::-1]a cada um de seus elementos (tupla) para revertê-la, de modo que cada tupla se torna (valor, chave) na nova lista expulsa do mapa. Finalmente, dict () cria um dict da nova lista.
Ilya Prokin

1

Usando loop : -

newdict = {} #Will contain reversed key:value pairs.

for key, value in zip(my_dict.keys(), my_dict.values()):
    # Operations on key/value can also be performed.
    newdict[value] = key


1

Adicionando uma solução no local:

>>> d = {1: 'one', 2: 'two', 3: 'three', 4: 'four'}
>>> for k in list(d.keys()):
...     d[d.pop(k)] = k
... 
>>> d
{'two': 2, 'one': 1, 'four': 4, 'three': 3}

No Python3, é fundamental que você use list(d.keys())porque dict.keysretorna uma visão das chaves. Se você estiver usando Python2, d.keys()é o suficiente.


1

A resposta de Hanan é a correta, pois cobre um caso mais geral (as outras respostas são meio enganosas para alguém que não tem conhecimento da situação duplicada). Uma melhoria na resposta de Hanan é usar setdefault:

mydict = {1:a, 2:a, 3:b}   
result = {}
for i in mydict:  
   result.setdefault(mydict[i],[]).append(i)
print(result)
>>> result = {a:[1,2], b:[3]}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.