Python - รายชื่อพจนานุกรมที่ไม่ซ้ำกัน


158

สมมติว่าฉันมีรายการพจนานุกรม:

[
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]

และฉันต้องการได้รับรายชื่อพจนานุกรมที่ไม่ซ้ำกัน (ลบที่ซ้ำกัน):

[
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]

ทุกคนสามารถช่วยฉันด้วยวิธีที่มีประสิทธิภาพสูงสุดในการบรรลุผลใน Python หรือไม่?


5
พจนานุกรมเหล่านี้มีขนาดกว้างขวางเพียงใด คุณต้องการการตรวจสอบคุณลักษณะแต่ละรายการเพื่อพิจารณาว่าซ้ำกันหรือตรวจสอบค่าเดียวในนั้นเพียงพอหรือไม่
gddc

dicts เหล่านี้มี 8 คีย์: คู่ของค่าและรายการมี 200 dicts พวกเขาได้รับ ID และปลอดภัยสำหรับฉันที่จะลบ dict ออกจากรายการหากค่า ID ที่พบนั้นซ้ำกัน
Limaaf


forzensetเป็นตัวเลือกที่มีประสิทธิภาพ set(frozenset(i.items()) for i in list)
Abhijeet

คำตอบ:


238

ดังนั้นทำให้ Dict idชั่วคราวที่สำคัญเป็น สิ่งนี้จะกรองสิ่งที่ซ้ำกัน The values()dict จะเป็นรายการ

ใน Python2.7

>>> L=[
... {'id':1,'name':'john', 'age':34},
... {'id':1,'name':'john', 'age':34},
... {'id':2,'name':'hanna', 'age':30},
... ]
>>> {v['id']:v for v in L}.values()
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

ใน Python3

>>> L=[
... {'id':1,'name':'john', 'age':34},
... {'id':1,'name':'john', 'age':34},
... {'id':2,'name':'hanna', 'age':30},
... ] 
>>> list({v['id']:v for v in L}.values())
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

ใน Python2.5 / 2.6

>>> L=[
... {'id':1,'name':'john', 'age':34},
... {'id':1,'name':'john', 'age':34},
... {'id':2,'name':'hanna', 'age':30},
... ] 
>>> dict((v['id'],v) for v in L).values()
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

@John La Rooy - วิธีการอย่างใดอย่างหนึ่งสามารถใช้เหมือนกันเพื่อลบพจนานุกรมจากรายการตามคุณลักษณะต่าง ๆ ลองนี้ แต่ดูเหมือนจะไม่ทำงาน> {v ['flight'] ['lon'] ['lat']: 'v สำหรับ' v in stream} .values ​​()
Jorge Vidinha

1
@JorgeVidinha สมมติว่าแต่ละคนสามารถส่งไปยัง str (หรือ unicode) ลองดู: {str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values()นี่แค่สร้างคีย์เฉพาะขึ้นอยู่กับค่าของคุณ ชอบ'MH370:-21.474370,86.325589'
whunterknight

4
@JorgeVidinha คุณสามารถใช้ tuple เป็นคีย์พจนานุกรมได้{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
John La Rooy

โปรดทราบว่าสิ่งนี้อาจเปลี่ยนลำดับของพจนานุกรมในรายการ! ใช้OrderedDictจากcollections list(OrderedDict((v['id'], v) for v in L).values()) หรือเรียงลำดับรายการผลลัพธ์หากใช้งานได้ดีกว่าสำหรับคุณ
gevra

หากคุณต้องการค่าทั้งหมดที่พิจารณาและไม่ใช่แค่ ID คุณสามารถใช้ที่list({str(i):i for i in L}.values())นี่เราใช้ str (i) เพื่อสร้างสตริงที่ไม่ซ้ำกันซึ่งแสดงถึงพจนานุกรมที่ใช้ในการกรองรายการที่ซ้ำกัน
DelboyJay

79

วิธีปกติในการค้นหาองค์ประกอบทั่วไปในชุดคือใช้setคลาสPython เพียงแค่เพิ่มองค์ประกอบทั้งหมดไปยังชุดจากนั้นแปลงชุดเป็น a listและบำที่ซ้ำกันจะหายไป

แน่นอนปัญหาคือ a set()สามารถมีได้เฉพาะรายการที่ hashable และ a dictไม่สามารถ hashable ได้

ถ้าผมมีปัญหานี้แก้ปัญหาของฉันจะแปลงแต่ละdictเป็นสตริงที่แสดงถึงที่dictแล้วเพิ่มสตริงทั้งหมดไปยังset()แล้วอ่านออกค่าสตริงเป็นและแปลงกลับไปlist()dict

การแสดงที่ดีของdictรูปแบบสตริงคือรูปแบบ JSON Python มีโมดูลในตัวสำหรับ JSON (เรียกว่าjsonแน่นอน)

ปัญหาที่เหลือคือองค์ประกอบใน a dictไม่ได้ถูกจัดเรียงและเมื่อ Python แปลงdictสตริงเป็น JSON คุณอาจได้รับสตริง JSON สองสายที่แสดงถึงพจนานุกรมเทียบเท่า แต่ไม่ใช่สตริงที่เหมือนกัน ทางออกที่ง่ายคือการผ่านการโต้แย้งเมื่อคุณเรียกsort_keys=Truejson.dumps()

แก้ไข: การแก้ปัญหานี้ได้รับการสมมติว่าได้รับdictอาจมีความแตกต่างกันส่วนหนึ่งส่วนใด หากเราสามารถสันนิษฐานได้ว่าทุกคนที่dictมี"id"ค่าเท่ากันจะจับคู่กันdictด้วย"id"ค่าเท่ากันนี่คือ overkill @ การแก้ปัญหา gnibbler จะเป็นเร็วและง่ายขึ้น

แก้ไข: ตอนนี้มีความคิดเห็นจากAndré Lima อย่างชัดเจนว่าถ้า ID ซ้ำกันก็ปลอดภัยที่จะถือว่าทั้งหมดdictนั้นซ้ำกัน ดังนั้นคำตอบนี้มากเกินไปและฉันแนะนำคำตอบของ @ gnibbler


ขอบคุณสำหรับความช่วยเหลือ steveha คำตอบของคุณจริงให้ฉันรู้บางอย่างที่ผมไม่ได้มีตั้งแต่ฉันเพิ่งเริ่มต้นกับงูหลาม =)
Limaaf

1
แม้ว่า overkill จะได้รับ ID ในกรณีนี้ แต่ก็ยังเป็นคำตอบที่ยอดเยี่ยม!
Josh Werts

8
สิ่งนี้ช่วยฉันได้เนื่องจากพจนานุกรมของฉันไม่มีคีย์และมีการระบุเฉพาะโดยรายการทั้งหมดของมัน ขอบคุณ!
ericso

วิธีนี้ใช้ได้ผลเกือบตลอดเวลา แต่อาจมีปัญหาเรื่องประสิทธิภาพในการขยายขนาด แต่ผู้เขียนที่ฉันคิดว่ารู้เรื่องนี้จึงแนะนำวิธีแก้ปัญหาด้วย "id" ข้อกังวลเกี่ยวกับประสิทธิภาพการทำงาน: โซลูชันนี้ใช้การเรียงลำดับเป็นสตริงจากนั้นยกเลิกการจัดลำดับ ... การซีเรียลไลซ์ / ดีซีเรียลไลซ์คือการคำนวณที่มีราคาแพงและมักไม่ขยายขนาดได้ดี (จำนวนรายการคือ n> 1e6 หรือพจนานุกรมแต่ละรายการ เพื่อรันหลาย ๆ ครั้ง> 1e6 หรือบ่อยครั้ง
เทรเวอร์บอยด์สมิ ธ

สั้น ๆ โซลูชันนี้แสดงตัวอย่างที่ยอดเยี่ยมของเหตุผลที่คุณต้องการออกแบบโซลูชันของคุณ ... เช่นถ้าคุณมี ID ที่ไม่เหมือนใคร ... จากนั้นคุณสามารถเข้าถึงข้อมูลได้อย่างมีประสิทธิภาพ ... หากคุณขี้เกียจ และไม่มีรหัสดังนั้นการเข้าถึงข้อมูลของคุณจึงมีราคาแพงกว่า
เทรเวอร์บอยด์สมิ ธ

21

ในกรณีที่พจนานุกรมมีการระบุเฉพาะโดยรายการทั้งหมด (ID ไม่พร้อมใช้งาน) คุณสามารถใช้คำตอบโดยใช้ JSON ต่อไปนี้เป็นทางเลือกที่ไม่ใช้ JSON และจะทำงานตราบใดที่ค่าพจนานุกรมทั้งหมดไม่เปลี่ยนรูป

[dict(s) for s in set(frozenset(d.items()) for d in L)]

19

คุณสามารถใช้ไลบรารี numpy (ใช้ได้กับ Python2.x เท่านั้น):

   import numpy as np 

   list_of_unique_dicts=list(np.unique(np.array(list_of_dicts)))

ในการทำให้ทำงานกับ Python 3.x (และเวอร์ชันล่าสุดของ numpy) คุณจะต้องแปลงอาร์เรย์ของ dicts เป็น array ของสตริงเช่น

list_of_unique_dicts=list(np.unique(np.array(list_of_dicts).astype(str)))

13
รับข้อผิดพลาดTypeError: unorderable types: dict() > dict()เมื่อทำสิ่งนี้ใน Python 3.5
Guillochon

16

นี่เป็นวิธีการแก้ปัญหาที่มีขนาดกะทัดรัดพอสมควร แต่ฉันสงสัยว่าไม่ได้มีประสิทธิภาพเป็นพิเศษ (เพื่อกล่าวอย่างอ่อนโยน):

>>> ds = [{'id':1,'name':'john', 'age':34},
...       {'id':1,'name':'john', 'age':34},
...       {'id':2,'name':'hanna', 'age':30}
...       ]
>>> map(dict, set(tuple(sorted(d.items())) for d in ds))
[{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}]

3
ล้อมรอบการmap()โทรด้วยlist()ใน Python 3 เพื่อรับรายการกลับมิฉะนั้นเป็นmapวัตถุ
dmn

ประโยชน์เพิ่มเติมของวิธีการนี้ในหลาม 3.6 ขึ้นไปก็คือการสั่งซื้อรายการที่มีการเก็บรักษาไว้
jnnnnn

7

เนื่องจากidมีเพียงพอสำหรับการตรวจจับรายการที่ซ้ำกันและidมีการแฮช: รัน 'em ผ่านพจนานุกรมที่มีปุ่มidเป็น ค่าสำหรับแต่ละคีย์คือพจนานุกรมต้นฉบับ

deduped_dicts = dict((item["id"], item) for item in list_of_dicts).values()

ใน Python 3 values()จะไม่ส่งคืนรายการ คุณจะต้องห่อด้านขวาทั้งหมดของนิพจน์list()นั้นและคุณสามารถเขียนเนื้อของนิพจน์ได้อย่างประหยัดมากขึ้นเพื่อความเข้าใจที่ถูกต้อง:

deduped_dicts = list({item["id"]: item for item in list_of_dicts}.values())

โปรดทราบว่าผลที่ได้อาจจะไม่อยู่ในลำดับเดียวกับต้นฉบับ หากเป็นความต้องการของคุณสามารถใช้แทนCollections.OrderedDictdict

นอกเหนือจากนี้อาจทำให้รู้สึกถึงการเก็บข้อมูลไว้ในพจนานุกรมที่ใช้idเป็นคีย์เพื่อเริ่มต้นด้วย


6
a = [
{'id':1,'name':'john', 'age':34},
{'id':1,'name':'john', 'age':34},
{'id':2,'name':'hanna', 'age':30},
]

b = {x['id']:x for x in a}.values()

print(b)

เอาท์พุท:

[{'อายุ': 34, 'id': 1, 'name': 'john'}, {'อายุ': 30, 'id': 2, 'name': 'hanna'}]


ในตัวอย่างเดียวกัน ฉันจะได้รับ dicts ที่มีเฉพาะ ID ที่คล้ายกันได้อย่างไร
user8162

@ user8162 คุณต้องการให้ผลลัพธ์เป็นอย่างไร
Yusuf X

บางครั้งฉันจะมี ID เดียวกัน แต่อายุต่างกัน ดังนั้นผลลัพธ์จะเป็น [{'อายุ': [34, 40], 'id': 1, 'name': ['john', Peter]}] กล่าวโดยย่อถ้า ID เหมือนกันให้รวมเนื้อหาของผู้อื่นเข้ากับรายการตามที่ฉันพูดถึง ขอบคุณล่วงหน้า.
user8162

1
b = {x ['id']: [y สำหรับ y ใน a หาก y ['id'] == x ['id']] สำหรับ x ใน a} เป็นวิธีหนึ่งในการจัดกลุ่มพวกเขาเข้าด้วยกัน
Yusuf X

4

การตอบคำถามJohn La Rooy ( Python - รายการพจนานุกรมที่ไม่ซ้ำกัน ) ทำให้มีความยืดหยุ่นมากกว่า

def dedup_dict_list(list_of_dicts: list, columns: list) -> list:
    return list({''.join(row[column] for column in columns): row
                for row in list_of_dicts}.values())

ฟังก์ชั่นการโทร:

sorted_list_of_dicts = dedup_dict_list(
    unsorted_list_of_dicts, ['id', 'name'])

4

เราสามารถทำได้ด้วย pandas

import pandas as pd
yourdict=pd.DataFrame(L).drop_duplicates().to_dict('r')
Out[293]: [{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

สังเกตเห็นความแตกต่างเล็กน้อยจากคำตอบที่ยอมรับ

drop_duplicates จะตรวจสอบคอลัมน์ทั้งหมดในหมีแพนด้าถ้าเหมือนกันทั้งหมดแล้วแถวที่จะลดลง

ตัวอย่างเช่น :

หากเราเปลี่ยนdictชื่อที่สองจากjohnเป็นpeter

L=[
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 1, 'name': 'peter', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]
pd.DataFrame(L).drop_duplicates().to_dict('r')
Out[295]: 
[{'age': 34, 'id': 1, 'name': 'john'},
 {'age': 34, 'id': 1, 'name': 'peter'},# here will still keeping the dict in the out put 
 {'age': 30, 'id': 2, 'name': 'hanna'}]

2

ใน python 3.6+ (สิ่งที่ฉันทดสอบ) เพียงใช้:

import json

#Toy example, but will also work for your case 
myListOfDicts = [{'a':1,'b':2},{'a':1,'b':2},{'a':1,'b':3}]
#Start by sorting each dictionary by keys
myListOfDictsSorted = [sorted(d.items()) for d in myListOfDicts]

#Using json methods with set() to get unique dict
myListOfUniqueDicts = list(map(json.loads,set(map(json.dumps, myListOfDictsSorted))))

print(myListOfUniqueDicts)

คำอธิบาย:เรากำลังทำแผนที่json.dumpsเพื่อเข้ารหัสพจนานุกรมเป็นวัตถุ json ซึ่งไม่เปลี่ยนรูป setแล้วสามารถนำมาใช้ในการผลิต iterable ของที่ไม่ซ้ำกัน immutables json.loadsสุดท้ายเราแปลงกลับไปเป็นตัวแทนของเราโดยใช้พจนานุกรม โปรดทราบว่าในขั้นต้นเราจะต้องเรียงลำดับโดยใช้แป้นเพื่อจัดเรียงพจนานุกรมในรูปแบบที่ไม่ซ้ำกัน สิ่งนี้ใช้ได้สำหรับ Python 3.6+ เนื่องจากพจนานุกรมเรียงลำดับตามค่าเริ่มต้น


1
อย่าลืมเรียงลำดับคีย์ก่อนที่จะดัมพ์ไปยัง JSON นอกจากนี้คุณยังไม่จำเป็นต้องแปลงก่อนที่จะทำlist set
นาธาน

2

ฉันได้สรุปรายการโปรดของฉันเพื่อลอง:

https://repl.it/@SmaMa/Python-List-of-unique-dictionaries

# ----------------------------------------------
# Setup
# ----------------------------------------------

myList = [
  {"id":"1", "lala": "value_1"},
  {"id": "2", "lala": "value_2"}, 
  {"id": "2", "lala": "value_2"}, 
  {"id": "3", "lala": "value_3"}
]
print("myList:", myList)

# -----------------------------------------------
# Option 1 if objects has an unique identifier
# -----------------------------------------------

myUniqueList = list({myObject['id']:myObject for myObject in myList}.values())
print("myUniqueList:", myUniqueList)

# -----------------------------------------------
# Option 2 if uniquely identified by whole object
# -----------------------------------------------

myUniqueSet = [dict(s) for s in set(frozenset(myObject.items()) for myObject in myList)]
print("myUniqueSet:", myUniqueSet)

# -----------------------------------------------
# Option 3 for hashable objects (not dicts)
# -----------------------------------------------

myHashableObjects = list(set(["1", "2", "2", "3"]))
print("myHashAbleList:", myHashableObjects)

1

วิธีแก้ปัญหาที่รวดเร็วและสกปรกเป็นเพียงการสร้างรายการใหม่

sortedlist = []

for item in listwhichneedssorting:
    if item not in sortedlist:
        sortedlist.append(item)

1

ฉันไม่ทราบว่าคุณต้องการให้ id ของ dicts ของคุณในรายการไม่ซ้ำกันหรือไม่ แต่ถ้าเป้าหมายคือการมีชุดของ dict ที่ unicity อยู่ในค่าของปุ่มทั้งหมด .. คุณควรใช้ tuples key เช่นนี้ ในความเข้าใจของคุณ:

>>> L=[
...     {'id':1,'name':'john', 'age':34},
...    {'id':1,'name':'john', 'age':34}, 
...    {'id':2,'name':'hanna', 'age':30},
...    {'id':2,'name':'hanna', 'age':50}
...    ]
>>> len(L)
4
>>> L=list({(v['id'], v['age'], v['name']):v for v in L}.values())
>>>L
[{'id': 1, 'name': 'john', 'age': 34}, {'id': 2, 'name': 'hanna', 'age': 30}, {'id': 2, 'name': 'hanna', 'age': 50}]
>>>len(L)
3

หวังว่ามันจะช่วยให้คุณหรือคนอื่นมีความกังวล ....


1

มีคำตอบมากมายที่นี่ดังนั้นขอเพิ่มอีก:

import json
from typing import List

def dedup_dicts(items: List[dict]):
    dedupped = [ json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in items)]
    return dedupped

items = [
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]
dedup_dicts(items)

0

ตัวเลือกตรงไปตรงมาสวย:

L = [
    {'id':1,'name':'john', 'age':34},
    {'id':1,'name':'john', 'age':34},
    {'id':2,'name':'hanna', 'age':30},
    ]


D = dict()
for l in L: D[l['id']] = l
output = list(D.values())
print output

0

คำตอบทั้งหมดที่กล่าวถึงในที่นี้ดี แต่ในบางคำตอบอาจพบข้อผิดพลาดหากรายการพจนานุกรมมีรายการซ้อนหรือพจนานุกรมดังนั้นฉันจึงเสนอคำตอบง่ายๆ

a = [str(i) for i in a]
a = list(set(a))
a = [eval(i) for i in a]

-1

การใช้งานนี้มีค่าใช้จ่ายหน่วยความจำเพียงเล็กน้อยในราคาที่ไม่ถูกกะทัดรัดเท่ากับส่วนที่เหลือ

values = [ {'id':2,'name':'hanna', 'age':30},
           {'id':1,'name':'john', 'age':34},
           {'id':1,'name':'john', 'age':34},
           {'id':2,'name':'hanna', 'age':30},
           {'id':1,'name':'john', 'age':34},]
count = {}
index = 0
while index < len(values):
    if values[index]['id'] in count:
        del values[index]
    else:
        count[values[index]['id']] = 1
        index += 1

เอาท์พุท:

[{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}]

1
คุณต้องทำการทดสอบนี้อีกเล็กน้อย การแก้ไขรายการขณะที่คุณทำซ้ำอาจไม่ทำงานอย่างที่คุณคาดหวัง
John La Rooy

@gnibbler เป็นจุดที่ดีมาก! ฉันจะลบคำตอบและทดสอบให้ละเอียดยิ่งขึ้น
Samy Vilar

ดูดีขึ้น คุณสามารถใช้ชุดเพื่อติดตามรหัสแทน dict พิจารณาเริ่มต้นindexที่len(values)และนับถอยหลังซึ่งหมายความว่าคุณสามารถลดลงindexว่าคุณdelหรือไม่ เช่นfor index in reversed(range(len(values))):
John La Rooy

@gnibbler น่าสนใจชุดทำมีลักษณะใกล้เคียงคงที่เหมือนพจนานุกรมหรือไม่
Samy Vilar

-4

นี่คือทางออกที่ฉันพบ:

usedID = []

x = [
{'id':1,'name':'john', 'age':34},
{'id':1,'name':'john', 'age':34},
{'id':2,'name':'hanna', 'age':30},
]

for each in x:
    if each['id'] in usedID:
        x.remove(each)
    else:
        usedID.append(each['id'])

print x

โดยทั่วไปคุณจะตรวจสอบว่า ID นั้นมีอยู่ในรายการหรือไม่ถ้าใช่ให้ลบพจนานุกรมหากไม่มีให้ผนวก ID ต่อท้ายรายการ


ฉันจะใช้ชุดแทนที่จะเป็นรายการสำหรับ usedID มันเป็นการค้นหาที่เร็วขึ้นและอ่านได้ง่ายขึ้น
happydave

ใช่ฉันไม่รู้เกี่ยวกับชุด ... แต่ฉันกำลังเรียนรู้ ... ฉันแค่มองไปที่คำตอบ @gnibbler ...
tabchas

1
คุณต้องทำการทดสอบนี้อีกเล็กน้อย การแก้ไขรายการในขณะที่คุณทำซ้ำอาจไม่ทำงานอย่างที่คุณคาดหวัง
John La Rooy

ใช่ฉันไม่เข้าใจว่าทำไมมันไม่ทำงาน ... ความคิดใด ๆ ที่ฉันทำผิด?
tabchas

ไม่ฉันเจอปัญหา ... มันแค่ว่าฉันไม่เข้าใจว่าทำไมมันถึงให้ปัญหา ... คุณรู้หรือไม่?
tabchas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.