Comment échanger des clés avec des valeurs dans un dictionnaire?

97

Je reçois un dictionnaire en entrée, et je voudrais renvoyer un dictionnaire dont les clés seront les valeurs de l'entrée et dont la valeur sera les clés d'entrée correspondantes. Les valeurs sont uniques.

Par exemple, disons que mon entrée est:

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

J'aimerais que ma sortie soit:

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

Pour clarifier, je voudrais que mon résultat soit l'équivalent de ce qui suit:

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

Un moyen pythonique pour y parvenir?

Roee Adler
la source
1
Voir stackoverflow.com/questions/1087694/… pour une question identique qui a une bonne réponse si vous utilisez Python 3
Stephen Edmonds
@Stephen: voyez la deuxième réponse la plus votée, c'est la même que celle acceptée dans la question à laquelle vous avez lié. La foule a préféré l'autre réponse cependant ...
Roee Adler
4
Python n'est pas perl, python n'est pas ruby. La lisibilité compte. Clairsemé vaut mieux que dense. Compte tenu de cela, toutes les méthodes de ces réponses sont tout simplement mauvaises ™; celui dans la question est la meilleure voie à suivre.
o0 '.
3
duplication possible de Python inverser / inverser une cartographie
Cory

Réponses:

148

Python 2:

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

Python 3 (merci à @erik):

res = dict((v,k) for k,v in a.items())
liori
la source
14
Bien que cela semble correct, il est vraiment bon d'ajouter une explication de son fonctionnement plutôt que simplement le code.
Will
4
Et si les valeurs ne sont pas uniques? Alors les clés devraient être une liste ... par exemple: d = {'a': 3, 'b': 2, 'c': 2} {v: k pour k, v dans d.iteritems ()} { 2: 'b', 3: 'a'} devrait être {2: ['b', 'c'], 3: 'a'}
Hanan Shteingart
4
@HananShteingart: les valeurs déclarées de la question du PO sont uniques. Veuillez créer un message de question distinct pour votre cas (et de préférence le lier ici pour d'autres personnes).
liori
le code python2 fonctionne ... mais la compréhension de la liste manque le [et ]. une compréhension de liste ne nécessite-t-elle pas le [et ]?
Trevor Boyd Smith
@TrevorBoydSmith: ce n'est pas une compréhension de liste, c'est une expression de générateur .
liori
55
new_dict = dict(zip(my_dict.values(), my_dict.keys()))
Javier
la source
4
Les valeurs () et les clés () sont-elles vraiment garanties d'avoir le même ordre?
Lennart Regebro
1
oui, de python.org/dev/peps/pep-3106 La spécification implique que l'ordre dans lequel les éléments sont renvoyés par .keys (), .values ​​() et .items () est le même (tout comme il l'était en Python 2.x), car l'ordre est entièrement dérivé de l'itérateur de dict (qui est vraisemblablement arbitraire mais stable tant qu'un dict n'est pas modifié). mais cette réponse doit appeler my_dict deux fois (une pour les valeurs, une pour les clés). ce n'est peut-être pas idéal.
sunqiang
4
Oui, cette réponse répète deux fois le dict. La réponse de sunqiang est préférable pour un gros dictionnaire car elle ne nécessite qu'une seule itération.
Carl Meyer
@Carl Meyer: d'accord, aussi, il utilise itertools qui sont bien meilleurs pour les grands ensembles de données. même si je me demande si l'appel final à dict () est également en streaming, ou s'il rassemble d'abord la liste des paires entières
Javier
@CarlMeyer en plus pour n> 1e6 (ou 1e9), l'utilisation de la mémoire sera également très importante ... et ralentira également cela un tas.
Trevor Boyd Smith
47

À partir de Python 2.7, y compris 3.0+, il existe une version sans doute plus courte et plus lisible:

>>> my_dict = {'x':1, 'y':2, 'z':3}
>>> {v: k for k, v in my_dict.items()}
{1: 'x', 2: 'y', 3: 'z'}
SilentGhost
la source
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
la source
3
Pas dans la question d'origine, je suis juste curieux de savoir ce qui se passera si vous aviez des valeurs en double dans le dictionnaire d'origine et que vous échangiez ensuite des clés / valeurs avec cette méthode?
Andre Miller
2
@Andre Miller: Il prend la dernière occurrence de la clé particulière: dict (((1,3), (1,2))) == {1: 2}
balpha
2
les doublons seront écrasés par la dernière dupe rencontrée.
Christopher
2
@Andre Miller: Et comme d.items () renvoie les éléments dans un ordre arbitraire, vous obtenez une clé arbitraire pour les valeurs en double.
Ants Aasma
Je pense qu'il faudra la dernière paire clé / valeur qu'il a trouvée. C'est comme un ['x'] = 3. Ensuite, vous définissez un ['x'] = 4.
riza
28

Vous pouvez utiliser les compréhensions de dict :

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

Modifié: pour Python 3, utilisez à la a.items()place de a.iteritems(). Des discussions sur les différences entre eux peuvent être trouvées dans les iteritems en Python sur SO.

Akavall
la source
18

Tu pourrais essayer:

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

Attention, vous ne pouvez pas «inverser» un dictionnaire si

  1. Plusieurs clés partagent la même valeur. Par exemple {'one':1,'two':1}. Le nouveau dictionnaire ne peut avoir qu'un seul élément avec clé 1.
  2. Une ou plusieurs des valeurs ne sont pas contrôlables. Par exemple {'one':[1]}. [1]est une valeur valide mais pas une clé valide.

Voir ce fil sur la liste de diffusion python pour une discussion sur le sujet.

Alasdair
la source
Également +1 à propos de la remarque concernant la garantie que les valeurs du dict original sont uniques; sinon vous aurez des écrasements dans le dict "inversé" ... et cela (je viens de trouver cela à mes dépens) peut provoquer des bugs délicats dans votre code!
monojohnny
15

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

pkit
la source
4
dict ne garantit pas que ses valeurs () et keys () renverront les éléments dans le même ordre. De plus, keys (), values ​​() et zip () renvoient une liste, où un itérateur serait suffisant.
liori
19
@liori: Vous vous trompez. dict garantit que ses valeurs () et keys () seront dans le même ordre, si vous ne modifiez pas le dict entre les appels à values ​​() et keys () bien sûr. La documentation indique qu'ici: (lire la partie "Note": docs.python.org/library/stdtypes.html#dict.items ) "Si items (), keys (), values ​​(), iteritems (), iterkeys ( ), et itervalues ​​() sont appelés sans modification du dictionnaire, les listes correspondent directement. "
nosklo
1
Ok, alors je me trompe ... Je n'ai pas vérifié les documents en ligne. Merci de l'avoir signalé.
liori
Vous pouvez utiliser l'itérateur itertools.izip au lieu de zip pour rendre cette réponse plus efficace.
Alasdair
Et les iterkeys et itervalues. Mais pourrait tout aussi bien utiliser iteritems ()
nosklo
15

La réponse principale actuelle suppose que les valeurs sont uniques, ce qui n'est pas toujours le cas. Et si les valeurs ne sont pas uniques? Vous perdrez des informations! Par exemple:

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

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

Les informations sur ont 'c'été complètement ignorées. Idéalement, cela devrait être quelque chose comme {2: ['b','c'], 3: ['a']}. C'est ce que fait l'implémentation du bas.

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
la source
cela devrait être la bonne réponse car elle couvre un cas plus général
Leon Rai
Merci pour ça! Je perdais des informations avec les autres solutions.
Cam le
14
new_dict = dict( (my_dict[k], k) for k in my_dict)

ou encore mieux, mais ne fonctionne qu'en Python 3:

new_dict = { my_dict[k]: k for k in my_dict}
balpha
la source
2
En fait, Dict Comprehensions ( PEP 274 ) fonctionne également avec Python 2.7.
Arseny
10

Une autre façon de développer la réponse d' Ilya Prokin est d'utiliser réellement la reversedfonction.

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

En substance, votre dictionnaire est itéré par (en utilisant .items()) où chaque élément est une paire clé / valeur, et ces éléments sont échangés avec la reversedfonction. Lorsque cela est passé au dictconstructeur, il les transforme en paires valeur / clé, ce que vous voulez.

Sunny Patel
la source
6

Suggestion d'amélioration pour la réponse de Javier:

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

Au lieu de d.keys()vous pouvez écrire simplement d, car si vous parcourez le dictionnaire avec un itérateur, il renverra les clés du dictionnaire pertinent.

Ex. pour ce comportement:

d = {'a':1,'b':2}
for k in d:
 k
'a'
'b'
shadow2097
la source
3

Peut être fait facilement avec la compréhension du dictionnaire:

{d[i]:i for i in d}
user10084443
la source
2
dict(map(lambda x: x[::-1], YourDict.items()))

.items()renvoie une liste de tuples de (key, value). map()parcourt les éléments de la liste et applique lambda x:[::-1]à chacun son élément (tuple) pour l'inverser, de sorte que chaque tuple devient (value, key)dans la nouvelle liste crachée hors de la carte. Enfin, dict()crée un dict à partir de la nouvelle liste.

Ilya Prokin
la source
.items () renvoie une liste de tuples (clé, valeur). map () parcourt les éléments de la liste et applique lambda x:[::-1]à chacun son élément (tuple) pour l'inverser, de sorte que chaque tuple devient (valeur, clé) dans la nouvelle liste crachée de map. Enfin, dict () crée un dict à partir de la nouvelle liste.
Ilya Prokin
1

Utilisation de la boucle : -

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
la source
1

Si vous utilisez Python3, c'est légèrement différent:

res = dict((v,k) for k,v in a.items())
Gravity Grave
la source
1

Ajout d'une solution sur place:

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

Dans Python3, il est essentiel que vous l'utilisiez list(d.keys())car dict.keysrenvoie une vue des clés. Si vous utilisez Python2, cela d.keys()suffit.

Timgeb
la source
1

La réponse de Hanan est la bonne car elle couvre un cas plus général (les autres réponses sont un peu trompeuses pour quelqu'un qui n'est pas au courant de la situation en double). Une amélioration de la réponse de Hanan est l'utilisation de 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
la source