পাইথনের তালিকায় সদৃশ ডিক সরান


153

আমার কাছে ডিক্টের একটি তালিকা রয়েছে এবং আমি অনুরূপ কী এবং মান জোড়া দিয়ে ডিক্টগুলি সরিয়ে দিতে চাই।

এই তালিকার জন্য: [{'a': 123}, {'b': 123}, {'a': 123}]

আমি এটি ফিরিয়ে দিতে চাই: [{'a': 123}, {'b': 123}]

আরেকটি উদাহরণ:

এই তালিকার জন্য: [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]

আমি এটি ফিরিয়ে দিতে চাই: [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]


আপনি যে প্রকৃত সমস্যাটি সমাধান করার চেষ্টা করছেন সে সম্পর্কে আরও কিছু বলতে পারেন? এটি দেখতে একটি বিজোড় সমস্যা বলে মনে হচ্ছে।
সৌভাগ্য

আমি কয়েকটি তালিকার তালিকা সংযুক্ত করছি এবং সেখানে নকল রয়েছে। সুতরাং আমার সেই নকলগুলি সরিয়ে ফেলতে হবে।
ব্রেন্ডেন

আমি ব্যবহার ছাড়াই একটি উত্তরে stackoverflow.com/questions/480214/… সমাধান পেয়েছিset()
সেবাস্তিয়ান ওয়াগনার

উত্তর:


242

এটা চেষ্টা কর:

[dict(t) for t in {tuple(d.items()) for d in l}]

কৌশলটি হ'ল অভিধানের তালিকাকে টিপলগুলির তালিকায় রূপান্তর করা যেখানে টিপলগুলি অভিধানের আইটেমগুলি ধারণ করে। যেহেতু টিপলগুলি হ্যাশ করা যায়, আপনি ডুপ্লিকেটগুলি মুছে ফেলতে পারেন set( এখানে একটি সেট বোধগম্যতা ব্যবহার করে , পুরানো পাইথনের বিকল্পটি হবে set(tuple(d.items()) for d in l)) এবং এর পরে, এর সাথে টিপলগুলি থেকে অভিধানগুলি পুনরায় তৈরি করুনdict

কোথায়:

  • l মূল তালিকা
  • d তালিকার একটি অভিধান
  • t অভিধান থেকে তৈরি টিপলগুলির মধ্যে একটি

সম্পাদনা: আপনি যদি অর্ডার সংরক্ষণ করতে চান তবে উপরের ওয়ান-লাইনারটি কাজ setকরবে না যেহেতু এটি করবে না। তবে কোডের কয়েকটি লাইনের সাহায্যে আপনি এটিও করতে পারেন:

l = [{'a': 123, 'b': 1234},
        {'a': 3222, 'b': 1234},
        {'a': 123, 'b': 1234}]

seen = set()
new_l = []
for d in l:
    t = tuple(d.items())
    if t not in seen:
        seen.add(t)
        new_l.append(d)

print new_l

উদাহরণ আউটপুট:

[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]

দ্রষ্টব্য: @ অ্যালেক্স দ্বারা চিহ্নিত হিসাবে এটি ঘটতে পারে যে একই কী এবং মান সহ দুটি অভিধান, একই টিউপলের ফলাফল না দেয়। যদি তারা কীগুলির ইতিহাস জুড়ে / মুছে ফেলার চেষ্টা করে তবেই এটি ঘটতে পারে। যদি এটি আপনার সমস্যার ক্ষেত্রে হয় তবে d.items()তার পরামর্শ অনুসারে বাছাই করার বিষয়টি বিবেচনা করুন ।


35
দুর্দান্ত সমাধান তবে এটিতে একটি বাগ রয়েছে: d.items()নির্দিষ্ট ক্রমে উপাদানগুলি ফেরত দেওয়ার গ্যারান্টি নেই। tuple(sorted(d.items()))একই কী-মানের জোড়গুলির জন্য আলাদা টিউপস না পেয়ে তা নিশ্চিত করার জন্য আপনার করা উচিত ।
অ্যালেক্সিস

@ অ্যালেক্সিস আমি কয়েকটি পরীক্ষা করেছি এবং আপনি সত্যই সঠিক। যদি এর মধ্যে অনেকগুলি কী যুক্ত করা হয় এবং পরে মুছে ফেলা হয় তবে তা কেস হতে পারে। ধন্যবাদ আপনার মন্তব্যের জন্য অনেক।
jcollado

কুল। আমি ভবিষ্যতের পাঠকদের সুবিধার জন্য আপনার উত্তরে এই সংশোধনটি যুক্ত করেছি যারা পুরো কথোপকথনটি না পড়তে পারে।
অ্যালেক্সিস

2
দ্রষ্টব্য, আপনি jsonযেমনটি করেছিলেন মডিউল থেকে সেই তালিকার তালিকায় লোড করলে এটি কাজ করবে না
ধ্রুব গুলতি

2
এটি এই ক্ষেত্রে একটি বৈধ সমাধান, তবে নেস্টেড অভিধানগুলির ক্ষেত্রে কাজ করবে না
লরেঞ্জো বেলি

51

তালিকা বোঝার উপর ভিত্তি করে আরেকটি ওয়ান-লাইনার:

>>> d = [{'a': 123}, {'b': 123}, {'a': 123}]
>>> [i for n, i in enumerate(d) if i not in d[n + 1:]]
[{'b': 123}, {'a': 123}]

এখানে যেহেতু আমরা dictতুলনাটি ব্যবহার করতে পারি , আমরা কেবলমাত্র প্রাথমিক উপাদানগুলির বাকি অংশগুলিতে রাখি না (এই ধারণাটি কেবল সূচকের মাধ্যমে অ্যাক্সেসযোগ্য n, সুতরাং এর ব্যবহার enumerate) of


2
এটি অভিধানের একটি তালিকার জন্যও কাজ করে যা প্রথম উত্তরটির তুলনায় তালিকাগুলি নিয়ে গঠিত
gbozee

1
উপরের উত্তরের পরিবর্তে আপনার অভিধানগুলিতে একটি মান হিসাবে আপনি যখন একটি অপ্রয়োজনীয় টাইপ করতে পারেন এটি এটিও কাজ করে।
স্টিভ রসিটার

1
এখানে, উদ্দেশ্যটি হ'ল নকল মানগুলি মুছে ফেলা, কী নয়, এই উত্তরের কোডটি দেখুন
জামিল নওদা

এটি অত্যন্ত অদক্ষ কোড। if i not in d[n + 1:]ডিক্টের পুরো তালিকার উপরে পুনরাবৃত্তি হয় ( nতবে এটি কেবল অপারেশনগুলির মোট সংখ্যা অর্ধেক করে দেয়) এবং আপনি আপনার অভিধানের প্রতিটি উপাদানটির জন্য যাচাই করছেন তাই এই কোডটি হে (এন ^ 2) সময়ের জটিলতা
বরিস

মান হিসাবে অভিধানের সাথে
অভিধানগুলির

22

অন্যান্য উত্তরগুলি কার্যকর হবে না যদি আপনি নেস্ট্রিযুক্ত জেএসওএন বিষয়গুলির মতো নেস্টেড ডিকশনারিগুলিতে পরিচালনা করছেন operating এই ক্ষেত্রে আপনি ব্যবহার করতে পারেন:

import json
set_of_jsons = {json.dumps(d, sort_keys=True) for d in X}
X = [json.loads(t) for t in set_of_jsons]

1
গ্রেট! কৌশলটি হ'ল ডিক অবজেক্টটি সরাসরি কোনও সেটে যুক্ত করা যায় না, এটি ডাম্প () দ্বারা জেসন অবজেক্টে রূপান্তর করা দরকার।
রেহান_ম্ন

18

যদি কোনও তৃতীয় পক্ষের প্যাকেজ ব্যবহার করা ঠিক থাকে তবে আপনি ব্যবহার করতে পারেন 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


15

কখনও কখনও পুরানো ধাঁচের লুপগুলি এখনও কার্যকর। এই কোডটি jcollado এর তুলনায় কিছুটা দীর্ঘ, তবে পড়তে খুব সহজ:

a = [{'a': 123}, {'b': 123}, {'a': 123}]
b = []
for i in range(0, len(a)):
    if a[i] not in a[i+1:]:
        b.append(a[i])

0মধ্যে range(0, len(a))প্রয়োজন নেই।
জুয়ান আন্তোনিও

12

আপনি যদি অর্ডার সংরক্ষণ করতে চান তবে আপনি এটি করতে পারেন

from collections import OrderedDict
print OrderedDict((frozenset(item.items()),item) for item in data).values()
# [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]

যদি অর্ডারটি কোনও ব্যাপার না হয় তবে আপনি এটি করতে পারেন

print {frozenset(item.items()):item for item in data}.values()
# [{'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]

দ্রষ্টব্য: পাইথন 3 এ, আপনার দ্বিতীয় পদ্ধতির dict_valuesতালিকার পরিবর্তে অ-সিরিয়ালযোগ্য আউটপুট দেয় । আপনাকে পুরো জিনিসটি আবার একটি তালিকায় ফেলে দিতে হবে। list(frozen.....)
saran3h

12

আপনি যদি আপনার কর্মপ্রবাহে পান্ডাস ব্যবহার করছেন তবে একটি বিকল্প হ'ল সরাসরি pd.DataFrameনির্মাণকারীর কাছে অভিধানের একটি তালিকা খাওয়ানো । তারপরে প্রয়োজনীয় ফলাফলের জন্য ব্যবহার drop_duplicatesএবং to_dictপদ্ধতিগুলি।

import pandas as pd

d = [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]

d_unique = pd.DataFrame(d).drop_duplicates().to_dict('records')

print(d_unique)

[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]

3

সর্বজনীন উত্তর নয় , তবে যদি আপনার তালিকাটি কিছু কী দ্বারা বাছাই করা হয়, যেমন:

l=[{'a': {'b': 31}, 't': 1},
   {'a': {'b': 31}, 't': 1},
 {'a': {'b': 145}, 't': 2},
 {'a': {'b': 25231}, 't': 2},
 {'a': {'b': 25231}, 't': 2}, 
 {'a': {'b': 25231}, 't': 2}, 
 {'a': {'b': 112}, 't': 3}]

তারপরে সমাধানটি এত সহজ:

import itertools
result = [a[0] for a in itertools.groupby(l)]

ফলাফল:

[{'a': {'b': 31}, 't': 1},
{'a': {'b': 145}, 't': 2},
{'a': {'b': 25231}, 't': 2},
{'a': {'b': 112}, 't': 3}]

নেস্টেড অভিধানগুলি দিয়ে কাজ করে এবং (স্পষ্টতই) অর্ডার সংরক্ষণ করে।


1

আপনি একটি সেট ব্যবহার করতে পারেন, তবে আপনার ডিক্টগুলি হ্যাশযোগ্য প্রকারে পরিণত করতে হবে।

seq = [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]
unique = set()
for d in seq:
    t = tuple(d.iteritems())
    unique.add(t)

অনন্য এখন সমান

set([(('a', 3222), ('b', 1234)), (('a', 123), ('b', 1234))])

ডিক্টস ফিরে পেতে:

[dict(x) for x in unique]

অর্ডার d.iteritems()গ্যারান্টিযুক্ত নয় - যাতে আপনি 'নকল' দিয়ে শেষ করতে পারেন unique
ড্যানোডোনভান

-1

দ্বিগুণ-নেস্টেড তালিকা বোঝার সাথে এখানে একটি দ্রুত এক-লাইন সমাধান রয়েছে (@ এমমানুয়েলের সমাধানের ভিত্তিতে)।

aপুরো ডিক মিলছে কিনা তা যাচাই না করে এটি প্রতিটি কী ডিকের একটি প্রাইমারি কী হিসাবে একটি উদাহরণ (উদাহরণস্বরূপ ) ব্যবহার করে

[i for n, i in enumerate(list_of_dicts) if i.get(primary_key) not in [y.get(primary_key) for y in list_of_dicts[n + 1:]]]

ওপি যা চেয়েছিল তা নয়, তবে এটিই আমাকে এই থ্রেডে নিয়ে আসে, তাই আমি বুঝতে পেরেছিলাম যে আমি যে সমাধানটি শেষ করেছি তা পোস্ট করব


-1

এত ছোট নয় তবে পড়তে সহজ:

list_of_data = [{'a': 123}, {'b': 123}, {'a': 123}]

list_of_data_uniq = []
for data in list_of_data:
    if data not in list_of_data_uniq:
        list_of_data_uniq.append(data)

এখন, তালিকার list_of_data_uniqঅনন্য প্রান্ত থাকবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.