Python - Eşsiz sözlüklerin listesi


158

Diyelim ki bir sözlük listem var:

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

ve benzersiz sözlüklerin bir listesini (kopyalarını kaldırarak) edinmeliyim:

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

Python'da bunu başarmanın en etkili yolu bana kimse yardımcı olabilir mi?


5
Bu sözlükler ne kadar kapsamlıdır? Yinelenenleri belirlemek için bireysel özellik kontrolüne mi ihtiyacınız var veya tek bir değeri kontrol etmek yeterli mi?
gddc

Bu dikte 8 anahtar var: değer çiftleri ve liste 200 dikte aldı. Aslında bir kimlik aldılar ve bulunan kimlik değeri yineleniyorsa, sözlüğü listeden kaldırmak benim için güvenlidir.
Limaaf


Yanıtlar:


238

Bu yüzden anahtar olan geçici bir dikte yapın id. Bu kopyaları filtreler. values()Dict liste olacak

Python2.7 sürümünde

>>> 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 içinde

>>> 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 sürümünde

>>> 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 - biri birden fazla özniteliğe dayalı bir listeden sözlükleri kaldırmak için bunu nasıl kullanabilir, bunu denedi ancak çalışmıyor gibi görünüyor> {v ['flight'] ['lon'] ['lat']: v akışta v}. değerler ()
Jorge Vidinha

1
@JorgeVidinha her birinin str (veya unicode) için kullanılabileceğini varsayarsak, şunu deneyin: {str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values()Bu sadece değerlerinize göre benzersiz bir anahtar oluşturur. Beğen'MH370:-21.474370,86.325589'
whunterknight

4
@JorgeVidinha, sözlük anahtarı olarak bir demet kullanabilirsiniz{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
John La Rooy

bunun listedeki sözlüklerin sırasını değiştirebileceğini unutmayın! kullanmak OrderedDictgelen collections list(OrderedDict((v['id'], v) for v in L).values()) Oluşan liste veya sıralama daha iyi sizin için o işleri
gevra

Yalnızca kullanabileceğiniz tanıtıcıya değil, dikkate alınan tüm değerlere ihtiyacınız varsa list({str(i):i for i in L}.values())Burada, kopyaları filtrelemek için kullanılan sözlüğü temsil eden benzersiz bir dize oluşturmak için str (i) kullanırız.
DelboyJay

79

Bir kümedeki sadece ortak öğeleri bulmanın genel yolu Python setsınıfını kullanmaktır . Sadece tüm elemanları sete ekleyin, sonra seti lista'ya dönüştürün ve kopyalar gitti.

Sorun, elbette, set()a'nın sadece yıkanabilir girdileri içerebilmesi ve dicta'nın yıkanamaz olmasıdır.

Bu sorun vardı, benim çözüm dicttemsil eden bir dizeye her dönüştürmek dict, sonra tüm dizeleri eklemek set()sonra dize değerlerini bir olarak okumak list()ve geri dönüştürmek olacaktırdict .

Bir dictdize formundaki iyi bir gösterim JSON biçimidir. Ve Python'un JSON için yerleşik bir modülü vardır ( jsonelbette denir ).

Geriye kalan sorun, bir öğedeki öğelerin dictsipariş edilmemesi ve Python'un dictbir JSON dizesine dönüştürmesi durumunda , eşdeğer sözlükleri temsil eden, ancak aynı dizeler olmayan iki JSON dizesi alabilirsiniz. Kolay çözüm, sort_keys=Truearadığınızda argümanı iletmektir json.dumps().

DÜZENLEME: Bu çözüm, belirli dictbir parçanın farklı bir bölüme sahip olabileceğini varsayıyordu . dictAynı "id"değere dictsahip her "id"birinin aynı değerle birbiriyle eşleşeceğini varsayabilirsek, bu aşırıdır; @ gnibbler'ın çözümü daha hızlı ve daha kolay olurdu.

EDIT: Şimdi André Lima açıkça kimliğini yinelenen varsa, bütünün dictyinelenen olduğunu varsaymak güvenli olduğunu söyleyen bir yorum var. Bu cevap aşırıya kaçtı ve @ gnibbler'ın cevabını tavsiye ediyorum.


Yardım için teşekkürler steveha. Cevabınız aslında bana Python =) ile başladığımdan beri sahip olmadığım bazı bilgiler verdi
Limaaf

1
Bu özel durumda kimlik verildiğinde aşırıya kaçmakla birlikte, bu hala mükemmel bir cevaptır!
Josh Werts

8
Sözlüğümde bir anahtar olmadığından ve yalnızca tüm girdileri tarafından benzersiz bir şekilde tanımlandığından bu bana yardımcı oluyor. Teşekkürler!
ericso

Bu çözüm çoğu zaman çalışır ancak ölçeklendirmeyle ilgili performans sorunları olabilir, ancak bence yazar bunu bilir ve bu nedenle "id" ile çözüm önerir. Performans kaygıları: Bu çözüm dizilemek için serileştirmeyi kullanır ve sonra serileştirmeyi kullanır ... serileştirme / serileştirme pahalı bir hesaplamadır ve genellikle iyi ölçeklenmez (öğe sayısı n> 1e6 veya her sözlük> 1e6 öğe veya her ikisini içerir) veya > 1e6 veya sık sık birçok kez yürütmek için.
Trevor Boyd Smith

Bu çözümün kısa bir kenara, çözümünüzü neden tasarlamak istediğinizin harika bir kanonik örneğini gösterdiği gibi ... yani benzersiz bir kimliğiniz varsa ... o zaman verilere verimli bir şekilde erişebilirsiniz ... tembelseniz ve bir kimliğiniz yoksa veri erişiminiz daha pahalıdır.
Trevor Boyd Smith

21

Sözlüklerin yalnızca tüm öğeler tarafından benzersiz bir şekilde tanımlanması durumunda (ID mevcut değildir) yanıtı JSON kullanarak kullanabilirsiniz. Aşağıdaki JSON kullanmayan bir alternatiftir ve tüm sözlük değerleri değişmez olduğu sürece çalışır

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

19

Numpy kütüphanesini kullanabilirsiniz (yalnızca Python2.x için çalışır):

   import numpy as np 

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

Python 3.x (ve numpy'nin son sürümleri) ile çalışmasını sağlamak için, dicts dizisini numpy dize dizisine dönüştürmeniz gerekir, örn.

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

13
TypeError: unorderable types: dict() > dict()Python 3.5 bunu yaparken hatayı alın .
Guillochon

16

İşte oldukça verimli olmadığından (hafifçe koymak için) şüphelenmeme rağmen oldukça kompakt bir çözüm:

>>> 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
Bir listeyi geri almak için map()aramayı list()Python 3 ile çevreleyin , aksi takdirde bir mapnesnedir.
dmn


7

Yana idçiftleri tespit etmek için yeterlidir ve idhashable geçerli: sahip bir sözlükten çalıştırmak onları idanahtar olarak. Her anahtarın değeri orijinal sözlüktür.

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

Python 3'te values()bir liste döndürmez; söz konusu ifadenin sağ tarafını içine sarmanız gerekir list()ve ifadenin etini bir diksiyon anlama olarak daha ekonomik bir şekilde yazabilirsiniz:

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

Sonucun büyük olasılıkla orijinalle aynı sırada olmayacağını unutmayın. Bu bir gereklilikse, Collections.OrderedDictyerine birdict .

Bir kenara, verileri başlangıçta as tuşunu kullanan bir sözlükte tutmak mantıklı olabilir 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)

çıktılar:

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


Aynı örnekte. yalnızca benzer kimlikler içeren diktleri nasıl edinebilirim?
user8162

@ user8162, çıktının nasıl görünmesini isterdiniz?
Yusuf X

Bazen aynı kimliğe, ancak farklı yaşlara sahip olacağım. bu nedenle çıktı [{'age': [34, 40], 'id': 1, 'name': ['john', Peter]}] olacaktır. Kısacası, kimlikler aynıysa, başkalarının içeriğini burada bahsettiğim gibi bir listeyle birleştirin. Şimdiden teşekkürler.
user8162

1
b = {x ['id']: [bir y'de y için y ['id'] == x ['id']] a'daki x için bunları bir arada gruplamanın bir yoludur.
Yusuf X

4

John La Rooy ( Python - Benzersiz sözlüklerin listesi ) cevabını genişleterek, biraz daha esnek hale getiriyor:

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

Arama fonksiyonu:

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

4

İle yapabiliriz 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'}]

Kabul cevabından biraz farklı dikkat edin.

drop_duplicates pandalardaki tüm sütunları kontrol edecek, eğer hepsi aynıysa satır bırakılacaktır.

Örneğin :

Biz 2 değiştirirseniz dictadını john için 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+ (test ettiklerim) içinde şunları kullanın:

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)

Açıklama:json.dumps sözlükleri değiştirilemeyen json nesneleri olarak kodlamak için eşleştiriyoruz . setdaha sonra bir dizi eşsiz değişmez üretmek için kullanılabilir . Son olarak, kullanarak sözlük temsilimize geri dönüş yaparız json.loads. Başlangıçta, sözlükleri benzersiz bir biçimde düzenlemek için anahtarlara göre sıralanması gerekir. Bu sözlükler varsayılan olarak sıralandığı için Python 3.6+ için geçerlidir.


1
JSON'a gitmeden önce anahtarları sıralamayı unutmayın. Ayrıca listyapmadan önce dönüştürmeniz gerekmez set.
Nathan

2

Denemek için favorilerimi özetledim:

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

Hızlı ve kirli bir çözüm sadece yeni bir liste oluşturmaktır.

sortedlist = []

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

1

Listedeki diktelerinizin kimliğinin benzersiz olmasını istiyorsanız bilmiyorum, ancak amaç birliğin tüm anahtarların değerlerinde olduğu bir dizi dikteye sahip olmak ise .. bunun gibi tuples anahtarını kullanmalısınız anladığınız zaman:

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

Umarım size veya endişeye sahip başka bir kişiye yardımcı olur ....


1

Burada bir sürü cevap var, o yüzden bir tane daha ekleyeyim:

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

Oldukça basit bir seçenek:

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

Burada bahsedilen tüm cevaplar iyidir, ancak bazı cevaplarda sözlük öğeleri iç içe liste veya sözlük varsa bir hata ile karşılaşabilir, bu yüzden basit bir cevap öneriyorum

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

-1

Heres kadar kompakt olmama pahasına çok az bellek yükü olan bir uygulama var.

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

çıktı:

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

1
Bunu biraz daha test etmeniz gerekiyor. Üzerinde yineleme yaparken listeyi değiştirmek her zaman beklediğiniz gibi çalışmayabilir
John La Rooy

@gnibbler çok iyi bir nokta! Cevabı silip daha ayrıntılı test edeceğim.
Samy Vilar

Daha iyi görünüyor. Karar yerine kimlikleri takip etmek için bir set kullanabilirsiniz. indexAt öğesini başlatmayı len(values)ve geriye doğru saymayı düşünün ; bu, her zaman indexsizin olup olmadığınızı azaltabileceğiniz anlamına gelir del. ör.for index in reversed(range(len(values))):
John La Rooy

@gnibbler ilginç, setler sözlükler gibi neredeyse sürekli görünüyor mu?
Samy Vilar

-4

Bulduğum çözüm bu:

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

Temel olarak, kimliğin listede olup olmadığını kontrol edersiniz, varsa sözlüğü silin, yoksa kimliği listeye ekleyin


UsedID için liste yerine bir küme kullanırdım. Bu daha hızlı bir arama ve daha okunabilir
happydave

Evet ben setler hakkında bilmiyordum ... ama öğreniyorum ... sadece @gnibbler cevabına bakıyordum ...
tabchas

1
Bunu biraz daha test etmeniz gerekiyor. Üzerinde yineleme yaparken listeyi değiştirmek her zaman beklediğiniz gibi çalışmayabilir
John La Rooy

Evet, neden işe yaramadığını anlamıyorum ... Yanlış yaptığım hakkında bir fikrin var mı?
tabchas

Hayır problemi yakaladım ... sadece neden bu problemi verdiğini anlamıyorum ... biliyor musun?
tabchas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.