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?

Roee Adler
fonte
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())
Liori
fonte
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()))
Javier
fonte
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'}
SilentGhost
fonte
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'}
Sunqiang
fonte
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
28

Você pode fazer uso de compreensões de dicionário :

res = {v: k for k, v in a.iteritems()}

Editado: Para Python 3, use em a.items()vez de a.iteritems(). As discussões sobre as diferenças entre eles podem ser encontradas em iteritens em Python no SO.

Akavall
fonte
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.

Alasdair
fonte
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()))

pkit
fonte
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
Hanan Shteingart
fonte
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}
balpha
fonte
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.

Sunny Patel
fonte
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'
shadow2097
fonte
3

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

{d[i]:i for i in d}
user10084443
fonte
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.

Ilya Prokin
fonte
.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
Devesh Saini
fonte
1

Se você estiver usando Python3, é um pouco diferente:

res = dict((v,k) for k,v in a.items())
Túmulo Gravitacional
fonte
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.

Timgeb
fonte
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]}
pegah
fonte