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

파이썬에서 이것을 달성하는 가장 효율적인 방법으로 나를 도울 수 있습니까?


5
이 사전은 얼마나 광범위합니까? 중복을 판별하기 위해 개별 속성 검사가 필요합니까, 아니면 단일 값을 검사하고 있습니까?
gddc

이 dicts에는 8 개의 키 : 값 쌍이 있고 목록에는 200 dicts가 있습니다. 그들은 실제로 ID를 얻었으며 발견 된 ID 값이 중복 된 경우 목록에서 dict를 제거하는 것이 안전합니다.
Limaaf


답변:


238

따라서 키가있는 임시 dict을 만드십시오 id. 중복 항목을 필터링합니다. 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 for v in stream} .values ​​()
Jorge Vidinha 2018 년

1
@JorgeVidinha 각각 str (또는 유니 코드)으로 캐스팅 될 수 있다고 가정하면 다음을 시도하십시오. {str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values()이는 값을 기반으로 고유 키를 만듭니다. 처럼'MH370:-21.474370,86.325589'
whunterknight

4
@JorgeVidinha, 튜플을 사전 키로 사용할 수 있습니다{(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

집합에서 공통 요소를 찾는 일반적인 방법은 Python의 set클래스 를 사용하는 것 입니다. 모든 요소를 ​​세트에 추가 한 다음 세트를로 변환 list하고 복제본은 사라집니다.

물론 문제 set()는에 해시 가능 항목 만 포함 할 수 있고 해시 가능 dict하지 않다는 것입니다.

이 문제가 발생하면 해결책은 각각 dict을 나타내는 문자열 로 변환 dict한 다음 모든 문자열을 추가 한 set()다음 문자열 값 을 a 로 읽고 list()다시 변환하는 것 dict입니다.

dict문자열 형식 의 올바른 표현 은 JSON 형식입니다. 그리고 파이썬에는 JSON을위한 내장 모듈이 있습니다 ( json물론 호출됩니다 ).

나머지 문제는의 요소 dict가 정렬되지 않았으며 Python dict이를 JSON 문자열 로 변환 할 때 동등한 사전을 나타내지 만 동일한 문자열이 아닌 두 개의 JSON 문자열을 얻을 수 있다는 것입니다. 쉬운 해결책은 sort_keys=True호출 할 때 인수를 전달하는 것 json.dumps()입니다.

편집 :이 솔루션은 주어진 dict부분이 다를 수 있다고 가정했습니다 . dict같은 "id"값을 가진 모든 사람 이 같은 값을 dict가진 다른 모든 사람 과 일치 한다고 가정 할 수 있다면 "id"이것은 과잉입니다. @gnibbler의 솔루션은 더 빠르고 쉽습니다.

편집 : 이제 André Lima의 ID가 중복 된 경우 전체 dict가 중복 이라고 가정하는 것이 안전하다는 의견이 있습니다 . 따라서이 답변은 과잉이며 @gnibbler의 답변을 권장합니다.


steveha의 도움에 감사드립니다. 방금 파이썬으로 시작한 이래로 당신의 대답은 실제로 가지고 있지 않은 지식을주었습니다.
Limaaf

1
이 특별한 경우에 과잉 ID를 부여했지만, 이것은 여전히 ​​훌륭한 해답입니다!
Josh Werts

8
사전에 키가 없으며 모든 항목으로 만 고유하게 식별되므로 도움이됩니다. 감사!
ericso

이 솔루션은 대부분 작동하지만 확장에 성능 문제가있을 수 있지만 필자는 필자가 이것을 알고 있으므로 "id"가있는 솔루션을 권장합니다. 성능 문제 :이 솔루션은 문자열로 직렬화 한 다음 직렬화 해제를 사용합니다 ... 직렬화 / 직렬화 해제는 값 비싼 계산이며 일반적으로 잘 확장되지 않습니다 (항목 수가 n> 1e6이거나 각 사전에> 1e6 항목 또는 둘 다 포함) 또는 이것을 1e6 이상 또는 자주 여러 번 실행하십시오.
Trevor Boyd Smith

간단히 말해서이 솔루션은 솔루션을 설계하려는 이유에 대한 훌륭한 표준 예를 보여줍니다. 예를 들어 고유 한 ID가있는 경우 ... 데이터에 효율적으로 액세스 할 수 있습니다 ... 게으른 경우 ID가 없으면 데이터 액세스가 더 비쌉니다.
Trevor Boyd Smith

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 배열을 numpy 문자열 배열로 변환해야합니다.

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
파이썬 3에서 map()호출을 둘러싼 list()목록을 얻으려면 그렇지 않으면 map객체입니다.
dmn

python 3.6+에서이 접근법의 추가 이점은 목록 순서가 유지
된다는 것입니다.

7

이 때문에 id중복을 검출하기위한 충분하고,이 id해쉬 인 다음을 가지고 사전을 통해 실행 '안에 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.OrderedDict대신 사용할 수 있습니다 dict.

따로, as 키를 사용하는 사전에 데이터를 보관 하는 것이 좋습니다 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)

출력 :

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


같은 예에서. 비슷한 ID 만 포함하는 dicts를 어떻게 얻을 수 있습니까?
user8162

@ user8162, 출력 결과는 어떻습니까?
Yusuf X

때때로, 나는 같은 신분을 가지지 만 나이는 다릅니다. 따라서 출력은 [{ 'age': [34, 40], 'id': 1, 'name': [ 'john', Peter]}]입니다. 요컨대, ID가 동일하면 여기에 언급 된대로 다른 내용을 목록에 결합하십시오. 미리 감사드립니다.
user8162

1
b = {x [ 'id'] : [y의 경우 y의 경우 y [ 'id'] == x [ 'id']]의 경우 a}는 x를 함께 그룹화하는 한 가지 방법입니다.
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그런 다음 고유 한 불변 의 이터 러블을 생성하는 데 사용할 수 있습니다 . 마지막으로를 사용하여 사전 표현으로 다시 변환 json.loads합니다. 처음에는 사전을 고유 한 형식으로 정렬하기 위해 키를 기준으로 정렬해야합니다. 사전은 기본적으로 정렬되므로 Python 3.6 이상에서 유효합니다.


1
JSON으로 덤프하기 전에 키를 정렬해야합니다. 또한하기 list전에 로 변환 할 필요가 없습니다 set.
Nathan

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

목록에서 당신의 dicts의 id 만 독창적이기를 원하는지는 모르겠지만, 목표가 unicity가 모든 키의 값에있는 dict 세트를 갖는 것이라면 튜플 키를 사용해야합니다. 당신의 이해에서 :

>>> 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 2016 년

더 좋아 보인다. 세트 대신 dict 대신 ID를 추적 할 수 있습니다. indexat을 시작하고 len(values)거꾸로 세는 것을 고려하십시오. 즉, 당신은 항상 index여부를 줄일 수 있습니다 del. 예 :for index in reversed(range(len(values))):
John La Rooy

@gnibbler가 흥미 롭습니다. 세트는 사전처럼 거의 일정하게 유지됩니까?
Samy Vilar 2016 년

-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에 list 대신 set을 사용합니다. 더 빠른 조회 및 더 읽기 쉬운
happydave

예, 나는 세트에 대해 몰랐지만 ... 배우고 있습니다 ... 나는 단지 @gnibbler 답변을보고있었습니다 ...
tabchas

1
이것을 조금 더 테스트해야합니다. 반복하는 동안 목록을 수정하면 예상대로 작동하지 않을 수도 있습니다.
John La Rooy

그래도 왜 작동하지 않는지 이해가 안됩니다 ... 내가 잘못하고있는 아이디어가 있습니까?
tabchas 2016 년

아니요, 문제가 발생했습니다. 왜 문제를 일으키는 지 이해하지 못하는 것입니다. 아시나요?
tabchas 2012 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.