যদি কোনও তৃতীয় পক্ষের প্যাকেজ ব্যবহার করা ঠিক থাকে তবে আপনি ব্যবহার করতে পারেন 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}]
এটি মূল তালিকার ক্রম বজায় থাকে ও হিসাবে এছাড়াও অভিধান মত unhashable আইটেম সব ব্যবস্থা করতে সক্ষম ধীর আলগোরিদিম (ফিরে পতনশীল দ্বারা O(n*m)
যেখানে n
মূল তালিকায় উপাদান এবং হয় m
পরিবর্তে মূল তালিকায় অনন্য উপাদান O(n)
)। কী এবং মান উভয়ই হ্যাশযোগ্য হলে আপনি key
"স্বতন্ত্রতা-পরীক্ষা" (যাতে এটি এতে কাজ করে O(n)
) এর জন্য হ্যাশযোগ্য আইটেম তৈরি করতে সেই ফাংশনের যুক্তিটি ব্যবহার করতে পারেন ।
অভিধানের ক্ষেত্রে (যা আদেশের তুলনায় স্বতন্ত্র তুলনা করে) আপনাকে এটিকে অন্য কোনও ডেটা-কাঠামোর সাথে মানচিত্রের প্রয়োজন যা তুলনামূলকভাবে তুলনা করে frozenset
:
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
নোট করুন যে আপনার একটি সরল tuple
পদ্ধতি ব্যবহার করা উচিত নয় (বাছাই ছাড়াই) কারণ সমান অভিধানের অগত্যা একই ক্রম থাকে না (এমনকি পাইথন ৩.7 এও যেখানে সন্নিবেশ ক্রম - নিখুঁত আদেশ নয় - গ্যারান্টিযুক্ত):
>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False
এমনকি কীগুলি বাছাইযোগ্য না হলে টিউপলকে বাছাই করা কাজ করতে পারে না:
>>> d3 = {1: 1, 'a': 'a'}
>>> tuple(sorted(d3.items()))
TypeError: '<' not supported between instances of 'str' and 'int'
মাপকাঠি
আমি ভাবলাম যে এই পদ্ধতির পারফরম্যান্সটি কীভাবে তুলনা করে তা দেখতে দরকারী হতে পারে, তাই আমি একটি ছোট বেঞ্চমার্ক করেছি। মানদণ্ডের গ্রাফগুলি সময় নং তালিকাভুক্ত আকারের উপর ভিত্তি করে কোনও নকল নেই (যেটি নির্বিচারে নির্বাচিত হয়েছিল, রানটাইম উল্লেখযোগ্যভাবে পরিবর্তন হয় না যদি আমি কিছু বা প্রচুর নকল যোগ করি)। এটি একটি লগ-লগ প্লট তাই সম্পূর্ণ পরিসীমাটি কভার করা থাকে।
পরম সময়:
দ্রুততম পদ্ধতির সাথে সম্পর্কিত সময়গুলি:
Thefourtheye থেকে দ্বিতীয় পদ্ধতির দ্রুততম এখানে। unique_everseen
সঙ্গে পদ্ধতির key
ফাংশন, দ্বিতীয় স্থানে রয়েছে তবে এটি দ্রুততম পদ্ধতির যে সংরক্ষণ অর্ডারের। Jcollado এবং thefourtheye থেকে অন্যান্য পন্থাগুলি প্রায় তত দ্রুত। unique_everseen
কী ছাড়াই ব্যবহার করার পদ্ধতি এবং ইমানুয়েল এবং বৃশ্চিকের সমাধানগুলি দীর্ঘ তালিকার জন্য খুব ধীর এবং O(n*n)
পরিবর্তে আরও খারাপ আচরণ করে O(n)
। স্টিপকে এর পদ্ধতির সাথে json
নয় O(n*n)
তবে এটি অনুরূপের চেয়ে অনেক ধীরO(n)
পদ্ধতির ।
মানদণ্ডগুলি পুনরুত্পাদন করার কোড:
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)
সম্পূর্ণতার জন্য এখানে কেবল নকল রয়েছে এমন তালিকার সময় রয়েছে:
# 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)}
সময় ছাড়া উল্লেখযোগ্যভাবে পরিবর্তন করবেন না unique_everseen
থাকলে key
ফাংশন, এই ক্ষেত্রে দ্রুততম সমাধান পারে। তবে যে শুধু সেরা ক্ষেত্রে (তাই প্রতিনিধি না) unhashable মান যে ফাংশন জন্য কারণ এটা রানটাইম তালিকায় অনন্য মান পরিমাণ নির্ভর করে: O(n*m)
যা এই ক্ষেত্রে মাত্র 1 এবং এইভাবে এটি চালনা করে O(n)
।
দাবি অস্বীকার: আমি এর লেখক iteration_utilities
।