Come si scambiano le chiavi con i valori in un dizionario?


97

Ricevo un dizionario come input e desidero restituire un dizionario le cui chiavi saranno i valori dell'input e il cui valore saranno le chiavi di input corrispondenti. I valori sono unici.

Ad esempio, supponiamo che il mio input sia:

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

Vorrei che il mio output fosse:

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

Per chiarire, vorrei che il mio risultato fosse l'equivalente del seguente:

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

Qualche modo preciso e pitonico per ottenere questo risultato?


1
Vedi stackoverflow.com/questions/1087694/… per una domanda identica che ha una bella risposta se stai usando Python 3
Stephen Edmonds,

@Stephen: vedi la seconda risposta più votata, è la stessa accettata nella domanda a cui ti sei collegato. La folla ha preferito l'altra risposta però ...
Roee Adler,

4
Python non è perl, python non è rubino. La leggibilità conta. Sparse è meglio di dense. Detto questo, tutti i metodi di queste risposte sono semplicemente cattivi ™; quello nella domanda è il modo migliore per andare.
o0 '.

3
Cory

Risposte:


148

Python 2:

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

Python 3 (grazie a @erik):

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

14
Anche se questo sembra essere corretto, è davvero utile aggiungere una spiegazione di come funziona piuttosto che solo il codice.
Il

4
E se i valori non sono univoci? Quindi le chiavi dovrebbero essere un elenco ... ad esempio: d = {'a': 3, 'b': 2, 'c': 2} {v: k for k, v in d.iteritems ()} { 2: 'b', 3: 'a'} dovrebbe essere {2: ['b', 'c'], 3: 'a'}
Hanan Shteingart

4
@HananShteingart: i valori dichiarati della domanda dell'OP sono unici. Crea un post di domande separato per il tuo caso (e preferibilmente collegalo qui per altre persone).
liori

il codice python2 funziona ... ma la comprensione dell'elenco manca di [e ]. una lista di comprensione non richiede la [e ]?
Trevor Boyd Smith,

@TrevorBoydSmith: questa non è la comprensione della lista, questa è un'espressione generatrice .
liori

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

4
È davvero garantito che i valori () e le chiavi () abbiano lo stesso ordine?
Lennart Regebro

1
sì, da python.org/dev/peps/pep-3106 La specifica implica che l'ordine in cui gli elementi vengono restituiti da .keys (), .values ​​() e .items () è lo stesso (proprio come era in Python 2.x), perché l'ordine è tutto derivato dall'iteratore dict (che è presumibilmente arbitrario ma stabile fintanto che un dict non viene modificato). ma questa risposta deve chiamare my_dict due volte (una per i valori, una per le chiavi). forse questo non è l'ideale.
sunqiang

4
Sì, questa risposta ripete due volte il dict. La risposta di sunqiang è preferibile per un dizionario di grandi dimensioni poiché richiede solo un'iterazione.
Carl Meyer

@Carl Meyer: d'accordo, inoltre, sta usando itertools che sono molto migliori per grandi set di dati. anche se mi chiedo se anche la chiamata finale di dict () sia in streaming, o se prima assembla l'intera lista delle coppie
Javier

@CarlMeyer inoltre per n> 1e6 (o 1e9) anche l'utilizzo della memoria sarà molto grande ... e anche questo rallenterà un po '.
Trevor Boyd Smith,

47

Da Python 2.7 in poi, incluso 3.0+, c'è una versione probabilmente più breve e più leggibile:

>>> 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
Non nella domanda originale, sono solo curioso di cosa succederebbe se avessi valori duplicati nel dizionario originale e poi scambiassi chiave / valori con questo metodo?
Andre Miller

2
@Andre Miller: prende l'ultima occorrenza della chiave particolare: dict (((1,3), (1,2))) == {1: 2}
balpha

2
i duplicati verranno sovrascritti con l'ultimo duplicato incontrato.
Christopher

2
@Andre Miller: E poiché d.items () restituisce gli elementi in un ordine arbitrario, ottieni una chiave arbitraria per i valori duplicati.
Ants Aasma

Penso che prenderà l'ultima chiave, la coppia di valori che ha trovato. È come una ['x'] = 3. Quindi imposti una ['x'] = 4.
riza

28

Puoi utilizzare le comprensioni dict :

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

Modificato: per Python 3, usa a.items()invece di a.iteritems(). Discussioni sulle differenze tra loro possono essere trovate negli iteritem in Python su SO.


18

Potresti provare:

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

Fai attenzione che non puoi "invertire" un dizionario se

  1. Più di una chiave condivide lo stesso valore. Ad esempio {'one':1,'two':1}. Il nuovo dizionario può avere solo un elemento con chiave1 .
  2. Uno o più valori non sono modificabili. Ad esempio {'one':[1]}. [1]è un valore valido ma non una chiave valida.

Vedi questo thread sulla mailing list di Python per una discussione sull'argomento.


Inoltre +1 sulla nota per assicurarsi che i valori nel dict originale siano unici; altrimenti otterrai delle sovrascritture nel dict 'invertito' ... e questo (l'ho appena trovato a mie spese) può causare bug complicati nel tuo codice!
monojohnny

15

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


4
dict non garantisce che i suoi valori () e keys () restituiscano elementi nello stesso ordine. Inoltre, keys (), values ​​() e zip () restituiscono un elenco, in cui sarebbe sufficiente un iteratore.
liori

19
@liori: Ti sbagli. dict garantisce che i suoi valori () e keys () SARANNO nello stesso ordine, se non modifichi il dict tra le chiamate a values ​​() e keys () ovviamente. La documentazione afferma che qui: (leggi la parte "Note": docs.python.org/library/stdtypes.html#dict.items ) "Se items (), keys (), values ​​(), iteritems (), iterkeys ( ) e itervalues ​​() vengono chiamati senza modifiche al dizionario, le liste corrisponderanno direttamente. "
nosklo,

1
Ok, allora mi sbaglio ... Non ho controllato i documenti online. Grazie per aver indicato questo.
liori

Potresti usare l'iteratore itertools.izip invece di zip per rendere questa risposta più efficiente.
Alasdair

E iterkey e itervalues. Ma potrebbe anche usare iteritems ()
nosklo,

15

L'attuale risposta principale presume che i valori siano unici, il che non è sempre il caso. E se i valori non sono univoci? Perderai informazioni! Per esempio:

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

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

Le informazioni su sono 'c'state completamente ignorate. Idealmente dovrebbe essere qualcosa di simile{2: ['b','c'], 3: ['a']} . Questo è ciò che fa l'implementazione in basso.

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

questa dovrebbe essere la risposta corretta poiché copre un caso più generale
Leon Rai

Grazie per questo! Stavo perdendo informazioni con le altre soluzioni.
Cam

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

o anche meglio, ma funziona solo in Python 3:

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

2
In realtà Dict Comprehensions ( PEP 274 ) funziona anche con Python 2.7.
Arseny

10

Un altro modo per espandere la risposta di Ilya Prokin è utilizzare effettivamente la reversedfunzione.

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

In sostanza, il tuo dizionario viene iterato attraverso (usando .items()) dove ogni elemento è una coppia chiave / valore e quegli elementi vengono scambiati con la reversedfunzione. Quando questo viene passato al dictcostruttore, li trasforma in coppie valore / chiave che è quello che vuoi.


6

Suggerimento per un miglioramento per la risposta di Javier:

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

Invece di d.keys()te puoi scrivere solod , perché se scorri il dizionario con un iteratore, restituirà le chiavi del dizionario pertinente.

Ex. per questo comportamento:

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

3

Può essere fatto facilmente con la comprensione del dizionario:

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

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

.items()restituisce un elenco di tuple di (key, value). map()passa attraverso gli elementi della lista e applica lambda x:[::-1]a ogni suo elemento (tupla) per invertirla, così ogni tupla diventa (value, key)nella nuova lista sputata dalla mappa. Infine, dict()fa un dict dalla nuova lista.


.items () restituisce un elenco di tuple (chiave, valore). map () passa attraverso gli elementi della lista e applica lambda x:[::-1]a ogni suo elemento (tupla) per invertirla, così ogni tupla diventa (valore, chiave) nella nuova lista sputata dalla mappa. Infine, dict () crea un dict dal nuovo elenco.
Ilya Prokin

1

Utilizzando 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

Se stai usando Python3, è leggermente diverso:

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

1

Aggiunta di una soluzione sul posto:

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

In Python3, è fondamentale che tu lo usi list(d.keys())perché dict.keysrestituisce una vista delle chiavi. Se stai usando Python2, d.keys()è sufficiente.


1

La risposta di Hanan è quella corretta in quanto copre un caso più generale (le altre risposte sono un po 'fuorvianti per qualcuno inconsapevole della situazione duplicata). Un miglioramento alla risposta di Hanan sta usando 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]}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.