Bagaimana cara menukar kunci dengan nilai dalam kamus?


97

Saya menerima kamus sebagai masukan, dan ingin mengembalikan kamus yang kuncinya adalah nilai masukan dan nilainya akan menjadi tombol masukan yang sesuai. Nilainya unik.

Misalnya, masukan saya adalah:

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

Saya ingin keluaran saya menjadi:

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

Untuk memperjelas, saya ingin hasil saya setara dengan berikut ini:

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

Adakah cara Pythonic yang rapi untuk mencapai ini?


1
Lihat stackoverflow.com/questions/1087694/… untuk pertanyaan serupa yang memiliki jawaban bagus jika Anda menggunakan Python 3
Stephen Edmonds

@ Stephen: lihat jawaban kedua yang paling banyak dipilih, sama dengan jawaban yang diterima dalam pertanyaan yang Anda tautkan. Penonton lebih suka jawaban lain meskipun ...
Roee Adler

4
Python bukan perl, python bukan ruby. Keterbacaan itu penting. Sparse lebih baik daripada padat. Mengingat ini, semua metode jawaban ini hanya buruk ™; yang dimaksud adalah cara terbaik untuk pergi.
o0 '.

Jawaban:


149

Python 2:

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

Python 3 (terima kasih kepada @erik):

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

14
Meskipun ini tampaknya benar, sangat bagus untuk menambahkan penjelasan tentang cara kerjanya daripada hanya kode.
Akankah

4
Bagaimana jika nilai tidak unik? Maka kuncinya harus berupa daftar ... misalnya: d = {'a': 3, 'b': 2, 'c': 2} {v: k untuk k, v di d.iteritems ()} { 2: 'b', 3: 'a'} harus {2: ['b', 'c'], 3: 'a'}
Hanan Shteingart

4
@HananShteingart: pertanyaan OP menyatakan nilainya unik. Harap buat postingan pertanyaan terpisah untuk kasus Anda (dan sebaiknya tautkan di sini untuk orang lain).
liori

kode python2 berfungsi ... tetapi pemahaman daftar tidak memiliki [dan ]. apakah pemahaman daftar tidak memerlukan [dan ]?
Trevor Boyd Smith

@TrevorBoydSmith: ini bukan pemahaman daftar, ini adalah ekspresi generator .
liori

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

4
Apakah benar-benar values ​​() dan keys () dijamin memiliki urutan yang sama?
Lennart Regebro

1
ya, dari python.org/dev/peps/pep-3106 Spesifikasi menyiratkan bahwa urutan item dikembalikan oleh .keys (), .values ​​() dan .items () adalah sama (seperti di Python 2.x), karena urutannya berasal dari iterator dikt (yang mungkin sewenang-wenang tetapi stabil selama dikt tidak dimodifikasi). tetapi jawaban ini perlu memanggil my_dict dua kali (satu untuk nilai, satu untuk kunci). mungkin ini tidak ideal.
sunqiang

4
Ya, jawaban ini mengulangi dikt dua kali. Jawaban sunqiang lebih disukai untuk kamus besar karena hanya membutuhkan satu iterasi.
Carl Meyer

@Carl Meyer: setuju, juga, dia menggunakan itertools yang jauh lebih baik untuk dataset besar. meskipun saya bertanya-tanya apakah panggilan dict () terakhir juga sedang streaming, atau apakah itu pertama-tama mengumpulkan seluruh daftar pasangan
Javier

@CarlMeyer tambahan untuk n> 1e6 (atau 1e9) penggunaan memori juga akan sangat besar ... dan juga memperlambat ini banyak.
Trevor Boyd Smith

47

Dari Python 2.7 dan seterusnya, termasuk 3.0+, ada versi yang bisa dibilang lebih pendek dan lebih mudah dibaca:

>>> 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
Tidak dalam pertanyaan awal, saya hanya ingin tahu apa yang akan terjadi jika Anda memiliki nilai duplikat di kamus asli dan kemudian menukar kunci / nilai dengan metode ini?
Andre Miller

2
@Andre Miller: Dibutuhkan kemunculan terakhir dari kunci tertentu: dict (((1,3), (1,2))) == {1: 2}
balpha

2
duplikat akan ditimpa dengan dupe yang terakhir ditemui.
Christopher

2
@ Andre Miller: Dan karena d.items () mengembalikan item dalam urutan arbitrer, Anda mendapatkan kunci arbitrer untuk nilai duplikat.
Ants Aasma

Saya pikir itu akan mengambil kunci terakhir, pasangan nilai yang ditemukannya. Ini seperti ['x'] = 3. Kemudian Anda menyetel ['x'] = 4.
riza

28

Anda dapat menggunakan pemahaman dict :

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

Diedit: Untuk Python 3, gunakan a.items()sebagai pengganti a.iteritems(). Diskusi tentang perbedaan di antara mereka dapat ditemukan di iteritems dengan Python di SO.


18

Kamu bisa mencoba:

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

Berhati-hatilah karena Anda tidak dapat 'membalikkan' kamus jika

  1. Lebih dari satu kunci memiliki nilai yang sama. Misalnya {'one':1,'two':1}. Kamus baru hanya dapat memiliki satu item dengan kunci 1.
  2. Satu atau beberapa nilai tidak dapat diganggu. Misalnya {'one':[1]}. [1]adalah nilai yang valid tetapi bukan kunci yang valid.

Lihat utas ini di milis python untuk diskusi tentang subjek.


Juga +1 tentang catatan tentang memastikan nilai dalam dikt asli unik; jika tidak, Anda akan mendapatkan penimpaan dalam perintah 'dibalik' ... dan ini (saya baru saja menemukan ini untuk biaya saya) dapat menyebabkan bug rumit dalam kode Anda!
monojohnny

15

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


4
dict tidak menjamin bahwa values ​​() dan keys () akan mengembalikan elemen dalam urutan yang sama. Selain itu, keys (), values ​​() dan zip () mengembalikan daftar, di mana iterator sudah cukup.
liori

19
@liori: Kamu salah. dict menjamin bahwa values ​​() dan keys () AKAN berada pada urutan yang sama, jika Anda tidak mengubah dict antara panggilan ke values ​​() dan keys () tentunya. Dokumentasi menyatakan bahwa di sini: (baca bagian "Catatan": docs.python.org/library/stdtypes.html#dict.items ) "If items (), keys (), values ​​(), iteritems (), iterkeys ( ), dan itervalues ​​() dipanggil tanpa modifikasi yang mengganggu kamus, daftar akan langsung berhubungan. "
nosklo

1
Oke, kalau begitu saya salah ... Saya belum memeriksa dokumen online. Terima kasih telah menunjukkan ini.
liori

Anda dapat menggunakan iterator itertools.izip daripada zip untuk membuat jawaban ini lebih efisien.
Alasdair

Dan iterkeys dan itervalues. Tapi bisa juga menggunakan iteritems ()
nosklo

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

atau bahkan lebih baik, tetapi hanya berfungsi di Python 3:

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

2
Sebenarnya Dict Comprehensions ( PEP 274 ) bekerja dengan Python 2.7 juga.
Arseny

14

Jawaban terdepan saat ini mengasumsikan nilai-nilai unik yang tidak selalu terjadi. Bagaimana jika nilai tidak unik? Anda akan kehilangan informasi! Sebagai contoh:

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

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

Informasi tentang 'c'benar-benar diabaikan. Idealnya harus seperti itu {2: ['b','c'], 3: ['a']}. Inilah yang dilakukan implementasi bawah.

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

ini harus menjadi jawaban yang benar karena mencakup kasus yang lebih umum
Leon Rai

Terima kasih untuk ini! Saya kehilangan informasi dengan solusi lain.

10

Cara lain untuk memperluas tanggapan Ilya Prokin adalah dengan menggunakan reversedfungsi tersebut.

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

Intinya, kamus Anda diulang (menggunakan .items()) di mana setiap item adalah pasangan kunci / nilai, dan item tersebut ditukar dengan reversedfungsi. Ketika ini diteruskan ke dictkonstruktor, itu mengubahnya menjadi pasangan nilai / kunci yang Anda inginkan.


6

Saran perbaikan untuk jawaban Javier:

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

Alih-alih d.keys()Anda dapat menulis hanya d, karena jika Anda membaca kamus dengan iterator, itu akan mengembalikan kunci kamus yang relevan.

Ex. untuk perilaku ini:

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


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

.items()mengembalikan daftar tupel dari (key, value). map()menelusuri elemen daftar dan menerapkan lambda x:[::-1]ke setiap elemennya (tupel) untuk membalikkannya, sehingga setiap tupel menjadi (value, key)dalam daftar baru yang dikeluarkan dari peta. Terakhir, dict()buat dikt dari daftar baru.


.items () mengembalikan daftar tupel (kunci, nilai). map () melewati elemen daftar dan berlaku lambda x:[::-1]untuk setiap elemennya (tupel) untuk membalikkannya, sehingga setiap tupel menjadi (nilai, kunci) dalam daftar baru yang dikeluarkan dari peta. Terakhir, dict () membuat dikt dari daftar baru.
Ilya Prokin

1

Menggunakan 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

Jika Anda menggunakan Python3, ini sedikit berbeda:

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

1

Menambahkan solusi di tempat:

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

Di Python3, Anda harus menggunakan list(d.keys())karena dict.keysmengembalikan tampilan kunci. Jika Anda menggunakan Python2, d.keys()sudah cukup.


1

Jawaban Hanan benar karena mencakup kasus yang lebih umum (jawaban lain agak menyesatkan bagi seseorang yang tidak menyadari situasi duplikat). Perbaikan jawaban Hanan menggunakan 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]}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.