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

これらの辞書には8つのキーと値のペアがあり、リストには200の辞書があります。彼らは実際にIDを取得しており、見つかったID値が重複している場合は、リストからdictを削除しても安全です。
Limaaf


forzensetは効果的なオプションです。set(frozenset(i.items()) for i in list)
Abhijeet 2017

回答:


238

したがって、キーをにして一時的な辞書を作成しますid。これにより、重複が除外されます。values()辞書のリストになります

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ストリーム} .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 2016

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()し、文字列値をとして読み取り、list()に変換することdictです。

dict文字列形式での適切な表現は、JSON形式です。そしてPythonにはJSON用の組み込みモジュールがあります(jsonもちろん呼ばれます)。

残りの問題は、aの要素dictが順序付けされていないことであり、PythonがdictをJSON文字列に変換すると、同等の辞書を表すが同一の文字列ではない2つのJSON文字列を取得する場合があります。簡単な解決策はsort_keys=True、を呼び出すときに引数を渡すことですjson.dumps()

編集:このソリューションは、特定のdict部分が異なる可能性があることを前提としていました。dict同じ"id"値を持つすべてが同じ値をdict持つ他のすべてと一致すると仮定できる場合"id"、これはやりすぎです。@gnibblerのソリューションは、より速くて簡単です。

編集:IDが重複している場合、全体dictが重複していると想定しても安全であると明確に述べているAndréLimaからのコメントがあります。したがって、この答えはやりすぎです。@ gnibblerの答えをお勧めします。


ヘルプstevehaをありがとう。私はPythonで始めたばかりなので、あなたの答えは実際に私が持っていなかったいくつかの知識を与えました=)
Limaaf

1
この特定のケースではIDが過​​剰に与えられましたが、これは依然として優れた答えです!
ジョシュワーツ2013

8
私の辞書にはキーがなく、すべてのエントリによってのみ一意に識別されるため、これは私に役立ちます。ありがとう!
ericso 2014

このソリューションはほとんどの時間で機能しますが、スケールアップでパフォーマンスの問題が発生する可能性がありますが、著者はこれを知っているため、「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)で動作させるには、dictの配列を文字列のnumpy配列に変換する必要があります。例:

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

13
TypeError: unorderable types: dict() > dict()Python 3.5でこれを行うとエラーが発生します。
ギロション

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

Python 3.6以降でのこのアプローチのもう1つの利点は、リストの順序が保持されることです
jnnnnn '29年

7

以来id重複を検出するために十分であり、かつidハッシュ可能です:持っている辞書を介して実行奴らidキーとして。各キーの値は元の辞書です。

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

Python 3ではvalues()、リストを返しません。その式の右側全体をでラップする必要があります。またlist()、式の内容をより経済的にdict内包として記述できます。

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)

出力:

[{'年齢':34、 'id':1、 'name': 'john'}、{'age':30、 'id':2、 'name': 'hanna'}]


同じ例で。同様のIDのみを含む辞書を取得するにはどうすればよいですか?
user8162

@ user8162、出力はどのようにしたいですか?
ユスフX

IDは同じでも年齢が異なる場合があります。したがって、出力は[{'age':[34、40]、 'id':1、 'name':['john'、Peter]}]となります。つまり、IDが同じ場合は、ここで説明したように、他のコンテンツをリストに結合します。前もって感謝します。
user8162

1
b = {x ['id']:[y in a in y in y ['id'] == x ['id']] for x in a}は、それらをグループ化する1つの方法です。
ユスフ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 パンダのすべての列をチェックします。すべて同じ場合、行は削除されます。

例えば ​​:

2番目のdict名前をジョンからピーターに変更した場合

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
ネイサン

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のみを一意にする必要があるかどうかはわかりませんが、目標が、すべてのキーの値に一意性がある一連の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

ここにはたくさんの答えがあるので、もう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 2012年

よく見えます。辞書を使用する代わりに、セットを使用してIDを追跡できます。indexat を開始し、len(values)逆方向に数えることを検討してください。つまり、自分がそうであるindexかどうかにdelかかわらず、いつでもデクリメントできます。例えばfor index in reversed(range(len(values))):
John La Rooy、

@gnibbler興味深いですが、セットは辞書のようにほぼ一定のルックアップがありますか?
Samy Vilar 2012年

-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 2012年

いいえ、問題が発生しました...なぜその問題が発生するのか、理解できません...知っていますか?
tabchas 2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.