Üçüncü taraf bir paket kullanmak uygunsa, şunları kullanabilirsiniz iteration_utilities.unique_everseen
:
>>> from iteration_utilities import unique_everseen
>>> l = [{'a': 123}, {'b': 123}, {'a': 123}]
>>> list(unique_everseen(l))
[{'a': 123}, {'b': 123}]
Orijinal listenin sırasını korur ve ut, daha yavaş bir algoritmaya geri dönerek sözlükler gibi shahable öğelerini de işleyebilir ( orijinal listedeki öğeler ve orijinal listedeki benzersiz öğeler O(n*m)
nerede ). Hem anahtarların hem de değerlerin yıkanabilir olması durumunda , "benzersizlik testi" için yıkanabilir öğeler oluşturmak üzere bu işlevin bağımsız değişkenini kullanabilirsiniz (böylece çalışır ).n
m
O(n)
key
O(n)
Bir sözlük söz konusu olduğunda (sıradan bağımsız olarak karşılaştırır), söz konusu ifadeyi bunun gibi bir başka veri yapısıyla eşlemeniz gerekir, örneğin frozenset
:
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
tuple
Eşit sözlüklerin aynı sıraya sahip olması gerekmediğinden (sıralama olmadan) basit bir yaklaşım kullanmamanız gerektiğini unutmayın ( ekleme siparişinin - mutlak sipariş değil - garanti edildiği Python 3.7'de bile ):
>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False
Ve anahtarlar sıralanabilir değilse, diziyi sıralamak işe yaramayabilir:
>>> d3 = {1: 1, 'a': 'a'}
>>> tuple(sorted(d3.items()))
TypeError: '<' not supported between instances of 'str' and 'int'
Karşılaştırma
Bu yaklaşımların performansının nasıl karşılaştırıldığını görmenin yararlı olabileceğini düşündüm, bu yüzden küçük bir kıyaslama yaptım. Karşılaştırma grafikleri, kopyalar içermeyen bir listeye dayalı olarak zamana göre liste boyutudur (rasgele seçilmişse, birkaç veya daha fazla kopya eklersem çalışma zamanı önemli ölçüde değişmez). Bu bir günlük-log grafiğidir, böylece tüm aralık kapsanır.
Mutlak zamanlar:
En hızlı yaklaşıma göre zamanlamalar:
Dört gözün ikinci yaklaşımı burada en hızlısıdır. unique_everseen
İle yaklaşım key
fonksiyonu ancak korur sipariş etmenizi hızlı bir yaklaşım, ikinci sırada yer almaktadır. Jcollado ve dört gözün diğer yaklaşımları da neredeyse hızlıdır. Anahtarsız kullanılan yaklaşım unique_everseen
ve Emmanuel ve Scorpil'in çözümleri daha uzun listeler için çok yavaştır ve O(n*n)
bunun yerine çok daha kötü davranır O(n)
. stpk s ile yaklaşım json
değil O(n*n)
ama çok daha yavaşO(n)
yaklaşımlardan .
Karşılaştırma ölçütlerini yeniden oluşturma kodu:
from simple_benchmark import benchmark
import json
from collections import OrderedDict
from iteration_utilities import unique_everseen
def jcollado_1(l):
return [dict(t) for t in {tuple(d.items()) for d in l}]
def jcollado_2(l):
seen = set()
new_l = []
for d in l:
t = tuple(d.items())
if t not in seen:
seen.add(t)
new_l.append(d)
return new_l
def Emmanuel(d):
return [i for n, i in enumerate(d) if i not in d[n + 1:]]
def Scorpil(a):
b = []
for i in range(0, len(a)):
if a[i] not in a[i+1:]:
b.append(a[i])
def stpk(X):
set_of_jsons = {json.dumps(d, sort_keys=True) for d in X}
return [json.loads(t) for t in set_of_jsons]
def thefourtheye_1(data):
return OrderedDict((frozenset(item.items()),item) for item in data).values()
def thefourtheye_2(data):
return {frozenset(item.items()):item for item in data}.values()
def iu_1(l):
return list(unique_everseen(l))
def iu_2(l):
return list(unique_everseen(l, key=lambda inner_dict: frozenset(inner_dict.items())))
funcs = (jcollado_1, Emmanuel, stpk, Scorpil, thefourtheye_1, thefourtheye_2, iu_1, jcollado_2, iu_2)
arguments = {2**i: [{'a': j} for j in range(2**i)] for i in range(2, 12)}
b = benchmark(funcs, arguments, 'list size')
%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.style.use('ggplot')
mpl.rcParams['figure.figsize'] = '8, 6'
b.plot(relative_to=thefourtheye_2)
Tamlık için, yalnızca kopyaları içeren bir listenin zamanlaması şöyledir:
# this is the only change for the benchmark
arguments = {2**i: [{'a': 1} for j in range(2**i)] for i in range(2, 12)}
Zamanlamalar fonksiyon unique_everseen
olmadan önemli ölçüde değişmez key
, bu durumda en hızlı çözüm budur. Ancak, bu işlev, paylaşılamaz değerlere sahip bu işlev için en iyi durumdur (temsili değildir) çünkü çalışma zamanı, listedeki benzersiz değerlerin miktarına bağlıdır: O(n*m)
bu durumda sadece 1'dir ve bu nedenle çalışır O(n)
.
Feragatname: Ben yazarım iteration_utilities
.