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?

Roee Adler
sumber
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 '.
3
kemungkinan duplikat Python membalikkan / membalikkan pemetaan
Cory

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())
liori
sumber
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()))
Javier
sumber
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'}
SilentGhost
sumber
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
sumber
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.

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

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

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

Cerah Patel
sumber
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'
bayangan2097
sumber
3

Dapat dilakukan dengan mudah dengan pemahaman kamus:

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

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

Jika Anda menggunakan Python3, ini sedikit berbeda:

res = dict((v,k) for k,v in a.items())
Gravity Grave
sumber
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.

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