ฉันจะแลกเปลี่ยนคีย์กับค่าในพจนานุกรมได้อย่างไร


102

ฉันได้รับพจนานุกรมเป็นอินพุตและต้องการส่งคืนพจนานุกรมซึ่งคีย์จะเป็นค่าของอินพุตและค่าของใครจะเป็นคีย์อินพุตที่เกี่ยวข้อง ค่าเป็นเอกลักษณ์

ตัวอย่างเช่นสมมติว่าข้อมูลที่ฉันป้อนคือ:

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

ฉันต้องการให้ผลลัพธ์ของฉันเป็น:

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

เพื่อชี้แจงฉันต้องการให้ผลลัพธ์ของฉันเทียบเท่ากับสิ่งต่อไปนี้:

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

วิธี Pythonic ที่เรียบร้อยเพื่อให้บรรลุสิ่งนี้?


1
ดูstackoverflow.com/questions/1087694/…สำหรับคำถามที่เหมือนกันซึ่งมีคำตอบที่ดีหากคุณใช้ Python 3
Stephen Edmonds

@ สตีเฟน: ดูคำตอบที่ได้รับการโหวตมากที่สุดเป็นอันดับสองเหมือนกับคำตอบที่ได้รับการยอมรับในคำถามที่คุณเชื่อมโยง ฝูงชนต้องการคำตอบอื่น ๆ แม้ว่า ...
Roee Adler

4
Python ไม่ใช่ perl python ไม่ใช่ทับทิม จำนวนการอ่าน เบาบางดีกว่าหนาแน่น ด้วยเหตุนี้วิธีการทั้งหมดของคำตอบเหล่านี้จึงไม่ดี คำถามคือวิธีที่ดีที่สุด
o0 '

คำตอบ:


156

Python 2:

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

Python 3 (ขอบคุณ @erik):

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

15
แม้ว่าสิ่งนี้จะถูกต้อง แต่ก็เป็นการดีที่จะเพิ่มคำอธิบายว่ามันทำงานอย่างไรมากกว่าแค่รหัส
Will

4
จะเกิดอะไรขึ้นถ้าค่าไม่ซ้ำกัน? จากนั้นคีย์ควรเป็นรายการ ... ตัวอย่างเช่น d = {'a': 3, 'b': 2, 'c': 2} {v: k สำหรับ k, v ใน d.iteritems ()} { 2: 'b', 3: 'a'} ควรเป็น {2: ['b', 'c'], 3: 'a'}
Hanan Shteingart

4
@HananShteingart: ค่าที่ระบุไว้ของ OP นั้นไม่เหมือนใคร โปรดสร้างโพสต์คำถามแยกต่างหากสำหรับกรณีของคุณ (และควรลิงก์ที่นี่สำหรับบุคคลอื่น)
liori

รหัส python2 ใช้งานได้ ... แต่ความเข้าใจในรายการไม่มี[และ]. ความเข้าใจในรายการไม่จำเป็นต้องใช้[และ]?
Trevor Boyd Smith

@TrevorBoydSmith: นี้ไม่เข้าใจรายการนี้เป็นแสดงออกกำเนิด
liori

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

4
มีการรับประกันค่า () และคีย์ () ว่ามีลำดับเดียวกันหรือไม่
Lennart Regebro

1
ใช่จากpython.org/dev/peps/pep-3106ข้อกำหนดระบุว่าลำดับที่รายการถูกส่งคืนโดย .keys (), .values ​​() และ. items () เหมือนกัน (เช่นเดียวกับใน Python 2.x) เนื่องจากคำสั่งทั้งหมดมาจากตัวทำซ้ำตามคำสั่ง (ซึ่งน่าจะเป็นไปตามอำเภอใจ แต่มีเสถียรภาพตราบเท่าที่ไม่มีการแก้ไขคำสั่ง) แต่คำตอบนี้ต้องเรียก my_dict สองครั้ง (หนึ่งสำหรับค่าหนึ่งสำหรับคีย์) อาจจะไม่เหมาะ
sunqiang

4
ใช่คำตอบนี้วนซ้ำผ่านคำสั่งสองครั้ง คำตอบของ sunqiang นั้นดีกว่าสำหรับพจนานุกรมขนาดใหญ่เนื่องจากต้องใช้การวนซ้ำเพียงครั้งเดียว
Carl Meyer

@Carl Meyer: เห็นด้วยเขาใช้ itertools ซึ่งดีกว่ามากสำหรับชุดข้อมูลขนาดใหญ่ แม้ว่าฉันจะสงสัยว่าการเรียกใช้คำสั่งสุดท้าย () นั้นสตรีมด้วยหรือไม่หรือหากเป็นการรวมรายการคู่ทั้งหมดก่อน
Javier

@CarlMeyer นอกจากนี้สำหรับ n> 1e6 (หรือ 1e9) การใช้หน่วยความจำก็จะใหญ่มากเช่นกัน ... และยังทำให้สิ่งนี้ช้าลง
Trevor Boyd Smith

47

ตั้งแต่ Python 2.7 เป็นต้นไปรวมถึง 3.0+ มีเนื้อหาที่สั้นกว่าและอ่านง่ายกว่า:

>>> 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
ไม่ใช่ในคำถามเดิมฉันแค่อยากรู้ว่าจะเกิดอะไรขึ้นถ้าคุณมีค่าที่ซ้ำกันในพจนานุกรมต้นฉบับแล้วสลับคีย์ / ค่าด้วยวิธีนี้?
Andre Miller

2
@ แอนเดรมิลเลอร์: ใช้การเกิดครั้งสุดท้ายของคีย์เฉพาะ: dict (((1,3), (1,2))) == {1: 2}
balpha

2
รายการที่ซ้ำกันจะถูกเขียนทับด้วยการหลอกลวงที่พบล่าสุด
Christopher

2
@ อันเดรมิลเลอร์: และเนื่องจาก d.items () ส่งคืนรายการตามลำดับที่กำหนดคุณจะได้รับคีย์โดยพลการสำหรับค่าที่ซ้ำกัน
Ants Aasma

ฉันคิดว่าจะใช้คีย์คู่ค่าสุดท้ายที่พบ มันเหมือนกับ ['x'] = 3 จากนั้นคุณตั้งค่า ['x'] = 4
riza

28

คุณสามารถใช้ประโยชน์จากการเข้าใจตามคำสั่ง :

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

แก้ไข: สำหรับ Python 3 ให้ใช้a.items()แทนa.iteritems()ไฟล์. การสนทนาเกี่ยวกับความแตกต่างระหว่างพวกเขาสามารถพบได้ในiteritems ใน Pythonบน SO


18

คุณสามารถลอง:

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

ระวังว่าคุณไม่สามารถ 'ย้อนกลับ' พจนานุกรมได้ถ้า

  1. คีย์มากกว่าหนึ่งคีย์ใช้ค่าเดียวกัน ตัวอย่างเช่น{'one':1,'two':1}. 1พจนานุกรมใหม่เท่านั้นสามารถมีหนึ่งรายการที่มีคีย์
  2. ค่าอย่างน้อยหนึ่งค่าไม่สามารถเข้าถึงได้ ตัวอย่างเช่น{'one':[1]}. [1]เป็นค่าที่ถูกต้อง แต่ไม่ใช่คีย์ที่ถูกต้อง

ดูหัวข้อนี้ในรายชื่อผู้รับจดหมาย python สำหรับการสนทนาในหัวข้อนี้


+1 เกี่ยวกับหมายเหตุเกี่ยวกับการตรวจสอบให้แน่ใจว่าค่าในคำสั่งดั้งเดิมไม่ซ้ำกัน มิฉะนั้นคุณจะถูกเขียนทับในคำสั่ง 'ย้อนกลับ' ... และสิ่งนี้ (ฉันเพิ่งพบสิ่งนี้เป็นค่าใช้จ่ายของฉัน) อาจทำให้เกิดข้อบกพร่องที่ยุ่งยากในโค้ดของคุณ!
monojohnny

17

คำตอบนำหน้าปัจจุบันถือว่าค่าไม่ซ้ำกันซึ่งไม่ได้เป็นเช่นนั้นเสมอไป จะเกิดอะไรขึ้นถ้าค่าไม่ซ้ำกัน? คุณจะหลวมข้อมูล! ตัวอย่างเช่น:

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

ผลตอบแทน {2: 'b', 3: 'a'}ผลตอบแทน

ข้อมูลเกี่ยวกับ'c'ถูกละเว้นโดยสิ้นเชิง ตามหลักการแล้วควรมีลักษณะเช่นนี้{2: ['b','c'], 3: ['a']}นี้ นี่คือสิ่งที่การใช้งานด้านล่างทำ

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

นี่ควรเป็นคำตอบที่ถูกต้องเนื่องจากครอบคลุมกรณีทั่วไปมากกว่า
Leon Rai

ขอบคุณสำหรับสิ่งนี้! ฉันสูญเสียข้อมูลไปพร้อมกับโซลูชันอื่น ๆ

15

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


4
dict ไม่รับประกันว่าค่า () และคีย์ () จะส่งคืนองค์ประกอบในลำดับเดียวกัน นอกจากนี้คีย์ () ค่า () และ zip () จะส่งคืนรายการซึ่งตัววนซ้ำจะเพียงพอ
liori

19
@liori: คุณคิดผิด dict รับประกันว่าค่า () และคีย์ () จะอยู่ในลำดับเดียวกันหากคุณไม่ได้ปรับเปลี่ยนคำสั่งระหว่างการเรียกค่า () และคีย์ () แน่นอน เอกสารระบุว่าที่นี่: (อ่านส่วน "หมายเหตุ": docs.python.org/library/stdtypes.html#dict.items ) "หากรายการ (), คีย์ (), ค่า (), iteritems (), iterkeys ( ) และ itervalues ​​() ถูกเรียกโดยไม่มีการแทรกแซงการแก้ไขพจนานุกรมรายการจะสอดคล้องกันโดยตรง "
nosklo

1
โอเคฉันผิดเอง ... ฉันยังไม่ได้ตรวจสอบเอกสารออนไลน์ ขอบคุณที่ชี้เรื่องนี้
liori

คุณสามารถใช้ iterator itertools.izip แทน zip เพื่อให้คำตอบนี้มีประสิทธิภาพมากขึ้น
Alasdair

และ iterkeys และ itervalues แต่ก็สามารถใช้ iteritems () ได้เช่นกัน
nosklo

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

หรือดีกว่า แต่ใช้ได้เฉพาะใน Python 3:

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

2
ความเข้าใจ Dict จริง ( PEP 274 ) ทำงานร่วมกับ Python 2.7 เช่นกัน
Arseny

10

อีกวิธีหนึ่งในการขยายการตอบสนองของIlya Prokinคือการใช้reversedฟังก์ชันนี้จริงๆ

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

โดยพื้นฐานแล้วพจนานุกรมของคุณจะวนซ้ำผ่าน (โดยใช้.items()) โดยที่แต่ละรายการเป็นคู่คีย์ / ค่าและรายการเหล่านั้นจะสลับกับreversedฟังก์ชัน เมื่อสิ่งนี้ถูกส่งไปยังตัวdictสร้างมันจะเปลี่ยนเป็นคู่ค่า / คีย์ซึ่งเป็นสิ่งที่คุณต้องการ


6

คำแนะนำสำหรับการปรับปรุงสำหรับคำตอบของ Javier:

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

แทนที่จะd.keys()เขียนเฉยๆdเพราะถ้าคุณใช้พจนานุกรมด้วยตัววนซ้ำมันจะส่งคืนคีย์ของพจนานุกรมที่เกี่ยวข้อง

เช่น สำหรับพฤติกรรมนี้:

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


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

.items()(key, value)กลับรายการของสิ่งอันดับของ map()ผ่านองค์ประกอบของรายการและใช้lambda x:[::-1]กับแต่ละองค์ประกอบ (ทูเปิล) เพื่อย้อนกลับดังนั้นแต่ละทูเปิลจะ(value, key)อยู่ในรายการใหม่ที่พ่นออกมาจากแผนที่ สุดท้ายdict()สร้างคำสั่งจากรายการใหม่


.items () ส่งคืนรายการทูเปิล (คีย์, ค่า) map () ผ่านองค์ประกอบของรายการและใช้lambda x:[::-1]กับแต่ละองค์ประกอบ (ทูเปิล) เพื่อย้อนกลับดังนั้นแต่ละทูเปิลจะกลายเป็น (ค่า, คีย์) ในรายการใหม่ที่พ่นออกจากแผนที่ สุดท้าย dict () สร้างคำสั่งจากรายการใหม่
Ilya Prokin



1

การเพิ่มโซลูชันในสถานที่:

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

ใน Python3 เป็นสิ่งสำคัญที่คุณต้องใช้list(d.keys())เนื่องจากdict.keysส่งคืนมุมมองของคีย์ หากคุณใช้ Python2 d.keys()ก็เพียงพอแล้ว


1

คำตอบของ Hanan เป็นคำตอบที่ถูกต้องเนื่องจากครอบคลุมกรณีทั่วไปมากกว่า (คำตอบอื่น ๆ เป็นคำตอบที่ทำให้เข้าใจผิดสำหรับผู้ที่ไม่ทราบสถานการณ์ที่ซ้ำกัน) การปรับปรุงคำตอบของ Hanan คือการใช้ 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]}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.