¿Cómo intercambio claves con valores en un diccionario?


97

Recibo un diccionario como entrada y me gustaría devolver un diccionario cuyas claves serán los valores de la entrada y cuyo valor serán las claves de entrada correspondientes. Los valores son únicos.

Por ejemplo, digamos que mi entrada es:

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

Me gustaría que mi salida fuera:

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

Para aclarar, me gustaría que mi resultado fuera el equivalente a lo siguiente:

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

¿Alguna forma pitónica ordenada de lograr esto?


1
Vea stackoverflow.com/questions/1087694/… para una pregunta idéntica que tiene una buena respuesta si está usando Python 3
Stephen Edmonds

@Stephen: vea la segunda respuesta más votada, es la misma que la aceptada en la pregunta a la que se vinculó. Sin embargo, la multitud prefirió la otra respuesta ...
Roee Adler

4
Python no es perl, python no es ruby. La legibilidad cuenta. Es mejor escaso que denso. Dado esto, todos los métodos de estas respuestas son simplemente malos ™; el de la pregunta es el mejor camino a seguir.
o0 '.

3
Cory

Respuestas:


148

Python 2:

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

Python 3 (gracias a @erik):

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

14
Si bien esto parece ser correcto, es realmente bueno agregar una explicación de cómo funciona en lugar de solo el código.
Será el

4
¿Y si los valores no son únicos? Entonces las claves deberían ser una lista ... por ejemplo: d = {'a': 3, 'b': 2, 'c': 2} {v: k para k, v en d.iteritems ()} { 2: 'b', 3: 'a'} debería ser {2: ['b', 'c'], 3: 'a'}
Hanan Shteingart

4
@HananShteingart: los valores indicados por la pregunta del OP son únicos. Cree una publicación de preguntas separada para su caso (y preferiblemente vincúlela aquí para otras personas).
liori

el código python2 funciona ... pero a la comprensión de la lista le falta [y ]. ¿la comprensión de una lista no requiere el [y ]?
Trevor Boyd Smith

@TrevorBoydSmith: esto no es comprensión de lista, es una expresión generadora .
liori

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

4
¿Realmente se garantiza que los valores () y las claves () tengan el mismo orden?
Lennart Regebro

1
sí, de python.org/dev/peps/pep-3106 La especificación implica que el orden en el que los elementos son devueltos por .keys (), .values ​​() y .items () es el mismo (tal como estaba en Python 2.x), porque todo el orden se deriva del iterador dict (que es presumiblemente arbitrario pero estable siempre que no se modifique un dict). pero esta respuesta necesita llamar a my_dict dos veces (una para valores, otra para claves). tal vez esto no sea lo ideal.
sunqiang

4
Sí, esta respuesta repite el dict dos veces. La respuesta de sunqiang es preferible para un diccionario grande, ya que solo requiere una iteración.
Carl Meyer

@Carl Meyer: de acuerdo, también, está usando itertools que son mucho mejores para grandes conjuntos de datos. aunque me pregunto si la llamada final a dict () también se está transmitiendo, o si primero reúne toda la lista de pares
Javier

@CarlMeyer, además, para n> 1e6 (o 1e9), el uso de memoria también será muy grande ... y también ralentizará esto un montón.
Trevor Boyd Smith

47

Desde Python 2.7 en adelante, incluida la 3.0+, hay una versión posiblemente más corta y legible:

>>> 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
No en la pregunta original, solo tengo curiosidad por saber qué pasaría si tuviera valores duplicados en el diccionario original y luego intercambia claves / valores con este método.
Andre Miller

2
@Andre Miller: Toma la última aparición de la clave en particular: dict (((1,3), (1,2))) == {1: 2}
balpha

2
los duplicados se sobrescribirán con el último incauto encontrado.
Christopher

2
@Andre Miller: Y debido a que d.items () devuelve elementos en un orden arbitrario, obtienes una clave arbitraria para valores duplicados.
Hormigas Aasma

Creo que tomará la última clave, par de valores que encontró. Es como una ['x'] = 3. Luego establece una ['x'] = 4.
riza

28

Puede hacer uso de dict comprensiones :

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

Editado: para Python 3, use en a.items()lugar de a.iteritems(). Las discusiones sobre las diferencias entre ellos se pueden encontrar en iteritems en Python en SO.


18

Tu podrías intentar:

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

Tenga en cuenta que no puede 'revertir' un diccionario si

  1. Más de una clave comparte el mismo valor. Por ejemplo {'one':1,'two':1}. El nuevo diccionario solo puede tener un elemento con clave 1.
  2. Uno o más de los valores no se pueden clasificar. Por ejemplo {'one':[1]}. [1]es un valor válido pero no una clave válida.

Vea este hilo en la lista de correo de Python para una discusión sobre el tema.


También +1 sobre la nota sobre asegurarse de que los valores en el dict original sean únicos; de lo contrario, se sobrescribirán en el diccionario 'invertido' ... ¡y esto (lo acabo de encontrar por mi cuenta) puede causar errores complicados en su código!
monojohnny

15

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


4
dict no garantiza que sus valores () y claves () devolverán elementos en el mismo orden. Además, keys (), values ​​() y zip () devuelven una lista, donde un iterador sería suficiente.
liori

19
@liori: Estás equivocado. dict garantiza que sus valores () y claves () ESTARÁN en el mismo orden, si no modifica el dict entre llamadas a valores () y claves () por supuesto. La documentación establece que aquí: (lea la parte "Nota": docs.python.org/library/stdtypes.html#dict.items ) "Si elementos (), claves (), valores (), iteritems (), iterkeys ( ), e itervalues ​​() se llaman sin modificaciones en el diccionario, las listas se corresponderán directamente. "
nosklo

1
Ok, entonces me equivoco ... No he revisado los documentos en línea. Gracias por señalar esto.
liori

Puede usar el iterador itertools.izip en lugar de zip para que esta respuesta sea más eficiente.
Alasdair

Y iterkeys e itervalues. Pero también podría usar iteritems ()
nosklo

15

La respuesta principal actual asume que los valores son únicos, lo que no siempre es el caso. ¿Y si los valores no son únicos? ¡Perderás información! Por ejemplo:

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

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

La información sobre 'c'fue completamente ignorada. Idealmente debería haber sido algo así {2: ['b','c'], 3: ['a']}. Esto es lo que hace la implementación inferior.

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 debería ser la respuesta correcta ya que cubre un caso más general
Leon Rai

¡Gracias por esto! Estaba perdiendo información con las otras soluciones.
Cam

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

o incluso mejor, pero solo funciona en Python 3:

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

2
Actually Dict Comprehensions ( PEP 274 ) también funciona con Python 2.7.
Arseny

10

Otra forma de ampliar la respuesta de Ilya Prokin es utilizar la reversedfunción.

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

En esencia, su diccionario se itera (usando .items()) donde cada elemento es un par clave / valor, y esos elementos se intercambian con la reversedfunción. Cuando esto se pasa al dictconstructor, los convierte en pares de valor / clave, que es lo que desea.


6

Sugerencia de mejora para la respuesta de Javier:

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

En lugar de d.keys()puede escribir simplemente d, porque si revisa el diccionario con un iterador, devolverá las claves del diccionario correspondiente.

Ex. para este comportamiento:

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


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

.items()devuelve una lista de tuplas de (key, value). map()pasa por los elementos de la lista y aplica lambda x:[::-1]a cada uno su elemento (tupla) para revertirlo, por lo que cada tupla se convierte (value, key)en la nueva lista escupida fuera del mapa. Finalmente, dict()hace un dictamen de la nueva lista.


.items () devuelve una lista de tuplas (clave, valor). map () recorre los elementos de la lista y aplica lambda x:[::-1]a cada uno su elemento (tupla) para revertirlo, por lo que cada tupla se convierte en (valor, clave) en la nueva lista que se extrae del mapa. Finalmente, dict () hace un dict de la nueva lista.
Ilya Prokin

1

Usando bucle : -

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

Agregar una solución in situ:

>>> 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}

En Python3, es fundamental que utilice list(d.keys())porque dict.keysdevuelve una vista de las claves. Si está utilizando Python2, d.keys()es suficiente.


1

La respuesta de Hanan es la correcta ya que cubre un caso más general (las otras respuestas son un poco engañosas para alguien que desconoce la situación duplicada). Una mejora de la respuesta de Hanan es 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]}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.