Jak wymienić klucze z wartościami w słowniku?


101

Otrzymuję słownik jako dane wejściowe i chciałbym zwrócić słownik, którego klucze będą wartościami danych wejściowych i którego wartością będą odpowiadające im klucze wejściowe. Wartości są wyjątkowe.

Na przykład, powiedzmy, że moje dane wejściowe to:

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

Chciałbym, aby mój wynik był:

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

Aby wyjaśnić, chciałbym, aby mój wynik był odpowiednikiem następującego:

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

Czy jest jakiś fajny sposób na osiągnięcie tego w Pythonie?


1
Zobacz stackoverflow.com/questions/1087694/…, aby znaleźć identyczne pytanie, które ma dobrą odpowiedź, jeśli używasz Pythona 3
Stephen Edmonds

@Stephen: zobacz drugą najczęściej głosowaną odpowiedź, jest taka sama, jak zaakceptowana w pytaniu, z którym łączysz się. Tłum wolał jednak inną odpowiedź ...
Roee Adler

4
Python nie jest perlem, python nie jest rubinem. Liczy się czytelność. Rzadkie jest lepsze niż gęste. Biorąc to pod uwagę, wszystkie metody tych odpowiedzi są po prostu złe ™; ten, którego dotyczy pytanie, jest najlepszą drogą.
o0 '.

Odpowiedzi:


154

Python 2:

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

Python 3 (dzięki @erik):

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

15
Chociaż wydaje się to być poprawne, naprawdę dobrze jest dodać wyjaśnienie, jak to działa, a nie tylko kod.
Will

4
A jeśli wartości nie są wyjątkowe? Wtedy klucze powinny być listą ... na przykład: d = {'a': 3, 'b': 2, 'c': 2} {v: k for k, v in d.iteritems ()} { 2: 'b', 3: 'a'} powinno być {2: ['b', 'c'], 3: 'a'}
Hanan Shteingart

4
@HananShteingart: podane w pytaniu OP wartości są unikalne. Utwórz osobny post z pytaniem dla swojej sprawy (i najlepiej, aby połączyć go tutaj dla innych osób).
liori

kod python2 działa ... ale w zrozumieniu listy brakuje [i ]. czy rozumienie listy nie wymaga [i ]?
Trevor Boyd Smith

@TrevorBoydSmith: to nie jest rozumienie listy, to jest wyrażenie generatora .
liori

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

4
Czy naprawdę wartości () i klucze () mają taką samą kolejność?
Lennart Regebro,

1
tak, z python.org/dev/peps/pep-3106 Specyfikacja sugeruje, że kolejność, w jakiej elementy są zwracane przez .keys (), .values ​​() i .items () jest taka sama (tak jak w Pythonie 2.x), ponieważ kolejność pochodzi od iteratora dykta (który jest przypuszczalnie arbitralny, ale stabilny, o ile dykt nie jest modyfikowany). ale ta odpowiedź wymaga dwukrotnego wywołania my_dict (jeden dla wartości, jeden dla kluczy). może to nie jest idealne.
sunqiang

4
Tak, ta odpowiedź dwukrotnie przewija się przez dyktando. Odpowiedź sunqianga jest preferowana w przypadku dużego słownika, ponieważ wymaga tylko jednej iteracji.
Carl Meyer,

@Carl Meyer: zgadzam się, on również używa narzędzi itertools, które są dużo lepsze dla dużych zbiorów danych. chociaż zastanawiam się, czy ostatnie wywołanie dict () również jest przesyłane strumieniowo, czy też najpierw składa całą listę par
Javier

@CarlMeyer dodatkowo dla n> 1e6 (lub 1e9) użycie pamięci również będzie naprawdę duże ... i również spowolni to kilka.
Trevor Boyd Smith

47

Począwszy od Pythona 2.7, w tym 3.0+, istnieje prawdopodobnie krótsza, bardziej czytelna wersja:

>>> 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
Nie w pierwotnym pytaniu, jestem po prostu ciekawy, co się stanie, jeśli masz zduplikowane wartości w oryginalnym słowniku, a następnie zamieniasz klucz / wartości tą metodą?
Andre Miller,

2
@Andre Miller: Zajmuje ostatnie wystąpienie określonego klucza: dict (((1,3), (1,2))) == {1: 2}
balpha

2
duplikaty zostaną nadpisane ostatnim napotkanym duplikatem.
Christopher,

2
@Andre Miller: A ponieważ d.items () zwraca elementy w dowolnej kolejności, otrzymujesz dowolny klucz dla zduplikowanych wartości.
Ants Aasma,

Myślę, że zajmie to ostatnią znalezioną parę kluczy i wartości. To jest jak ['x'] = 3. Następnie ustawiasz ['x'] = 4.
riza


18

Możesz spróbować:

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

Pamiętaj, że nie możesz „odwrócić” słownika, jeśli

  1. Więcej niż jeden klucz ma tę samą wartość. Na przykład {'one':1,'two':1}. Nowy słownik może mieć tylko jedną pozycję z kluczem1 .
  2. Co najmniej jednej wartości nie można zhasować. Na przykład {'one':[1]}. [1]jest prawidłową wartością, ale nie jest prawidłowym kluczem.

Zobacz ten wątek na liście mailingowej Pythona, aby zapoznać się z dyskusją na ten temat.


Również +1 na temat uwagi o upewnieniu się, że wartości w oryginalnym dyktandzie są niepowtarzalne; w przeciwnym razie w „odwróconym” dyktandzie zostaną nadpisane ... a to (właśnie znalazłem to na swój koszt) może spowodować trudne błędy w kodzie!
monojohnny,

17

Obecna wiodąca odpowiedź zakłada, że ​​wartości są niepowtarzalne, co nie zawsze ma miejsce. A jeśli wartości nie są wyjątkowe? Stracisz informacje! Na przykład:

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

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

Informacja o 'c'została całkowicie zignorowana. Idealnie powinno być coś takiego {2: ['b','c'], 3: ['a']}. To właśnie robi implementacja na dole.

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

to powinna być prawidłowa odpowiedź, ponieważ obejmuje bardziej ogólny przypadek
Leon Rai

Dziękuję Ci za to! Z innymi rozwiązaniami traciłem informacje.

15

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


4
dict nie gwarantuje, że jego wartości () i keys () zwrócą elementy w tej samej kolejności. Ponadto klucze (), wartości () i zip () zwracają listę, w przypadku której iterator byłby wystarczający.
liori

19
@liori: Mylisz się. dict gwarantuje, że jego wartości () i keys () BĘDĄ w tej samej kolejności, jeśli oczywiście nie zmodyfikujesz dyktowania między wywołaniami wartości () i keys (). Dokumentacja stwierdza, że ​​tutaj: (przeczytaj część „Uwaga”: docs.python.org/library/stdtypes.html#dict.items ) „If items (), keys (), values ​​(), iteritems (), iterkeys ( ), a itervalues ​​() są wywoływane bez żadnych modyfikacji słownika, listy będą bezpośrednio odpowiadać. "
nosklo

1
Ok, więc się mylę ... Nie sprawdziłem dokumentów online. Dziękuję za wskazanie tego.
liori

Możesz użyć iteratora itertools.izip zamiast zip, aby ta odpowiedź była bardziej wydajna.
Alasdair

I klucze i wartości iteracyjne. Ale równie dobrze przydałby się iteritems ()
nosklo

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

lub nawet lepiej, ale działa tylko w Pythonie 3:

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

2
Właściwie Dict Compression ( PEP 274 ) działa również z Pythonem 2.7.
Arseny

10

Innym sposobem rozwinięcia odpowiedzi Ilyi Prokin jest faktyczne użycie reversedfunkcji.

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

W istocie, twój słownik jest iterowany (przy użyciu .items()), gdzie każdy element jest parą klucz / wartość, a te elementy są zamieniane z reversedfunkcją. Kiedy jest to przekazywane do dictkonstruktora, zamienia je w pary wartość / klucz, co jest tym, czego chcesz.


6

Propozycja ulepszenia odpowiedzi Javiera:

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

Zamiast d.keys()ciebie możesz po prostu pisaćd , ponieważ jeśli przejdziesz przez słownik z iteratorem, zwróci on klucze odpowiedniego słownika.

Dawny. dla tego zachowania:

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


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

.items()zwraca listę krotek (key, value). map()przechodzi przez elementy listy i stosuje się lambda x:[::-1]do każdego jego elementu (krotki), aby ją odwrócić, więc każda krotka trafia (value, key)na nową listę wyplutowaną z mapy. Na koniec dict()tworzy dyktando z nowej listy.


.items () zwraca listę krotek (klucz, wartość). map () przechodzi przez elementy listy i stosuje się lambda x:[::-1]do każdego jej elementu (krotki), aby ją odwrócić, więc każda krotka staje się (wartość, klucz) na nowej liście wyplutowanej poza mapę. Wreszcie dict () tworzy dyktando z nowej listy.
Ilya Prokin

1

Korzystanie z pętli : -

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

Dodawanie rozwiązania lokalnego:

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

W Python3 krytyczne znaczenie ma użycie, list(d.keys())ponieważ dict.keyszwraca widok kluczy. Jeśli używasz Python2, d.keys()wystarczy.


1

Odpowiedź Hanana jest prawidłowa, ponieważ obejmuje bardziej ogólny przypadek (inne odpowiedzi są trochę mylące dla kogoś, kto nie jest świadomy powielonej sytuacji). Ulepszeniem odpowiedzi Hanana jest użycie 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]}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.