¿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?

Roee Adler
fuente
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
posible duplicado de Python inversa / inversa a un mapeo
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())
liori
fuente
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()))
Javier
fuente
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'}
SilentGhost
fuente
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
fuente
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.

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

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

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

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

Se puede hacer fácilmente con la comprensión del diccionario:

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

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

Si está usando Python3, es ligeramente diferente:

res = dict((v,k) for k,v in a.items())
Tumba de gravedad
fuente
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.

timgeb
fuente
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]}
pegah
fuente