पायथन में सूची में डुप्लिकेट हुक्म निकालें


153

मेरे पास dicts की एक सूची है, और मैं समान कुंजी और मान जोड़े के साथ dicts को निकालना चाहूंगा।

इस सूची के लिए: [{'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}]


क्या आप हमें उस वास्तविक समस्या के बारे में बता सकते हैं जिसे आप हल करने की कोशिश कर रहे हैं? यह एक अजीब समस्या की तरह लगता है।
गफिन

मैं dicts की कुछ सूचियों का संयोजन कर रहा हूं और डुप्लिकेट हैं। इसलिए मुझे उन डुप्लिकेट्स को हटाने की जरूरत है।
ब्रेंडेन

मैं बिना किसी जवाब के stackoverflow.com/questions/480214/… के उपयोग में पाया गयाset()
सेबस्टियन वैगनर

जवाबों:


242

इसे इस्तेमाल करे:

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

रणनीति शब्दकोशों की सूची को tuples की सूची में बदलने के लिए है जहाँ tuples में शब्दकोष के आइटम होते हैं। चूंकि ट्यूल को हैशड किया जा सकता है, आप डुप्लिकेट को निकाल सकते हैं 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}]

नोट: जैसा कि @alexis द्वारा बताया गया है कि ऐसा हो सकता है कि एक ही कुंजी और मान वाले दो शब्दकोष, समान रूप से परिणाम न करें। ऐसा हो सकता है अगर वे एक अलग जोड़ने / हटाने कुंजी इतिहास के माध्यम से जाते हैं। यदि आपकी समस्या का मामला है, तो उसके d.items()सुझाव के अनुसार छंटाई पर विचार करें ।


35
अच्छा समाधान है लेकिन इसमें एक बग है: d.items()तत्वों को किसी विशेष क्रम में वापस करने की गारंटी नहीं है। आपको tuple(sorted(d.items()))यह सुनिश्चित करने के लिए करना चाहिए कि आपको एक ही कुंजी-मूल्य वाले जोड़े के लिए अलग-अलग ट्यूपल्स न मिलें।
एलेक्सिस

@alexis मैंने कुछ परीक्षण किए और आप वास्तव में सही हैं। यदि बाद में बहुत सारी कुंजियों को जोड़ दिया जाता है और हटा दिया जाता है, तो यह मामला हो सकता है। आपकी टिप्पणी के लिए बहुत बहुत धन्यवाद।
15 अक्टूबर को 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)।


2
यह उन शब्दकोशों की सूची के लिए भी काम करता है जिनमें पहले उत्तर की तुलना में सूचियों का समावेश होता है
gbozee

1
यह तब भी काम करता है जब आपके पास शीर्ष जवाब के विपरीत, आपके शब्दकोशों में मूल्य के रूप में एक उपलब्ध प्रकार हो सकता है।
स्टीव रॉसिटर

1
यहां, इसका उद्देश्य डुप्लिकेट मानों को हटाना है, कुंजी नहीं, इस उत्तर का कोड देखें
जमील नॉयडा

यह बहुत अक्षम कोड है। if i not in d[n + 1:](पूरी nतरह से संचालन की कुल संख्या को आधा कर देता है) ( लेकिन यह सिर्फ आपके शब्दकोश में हर तत्व के लिए जाँच कर रहा है) की पूरी सूची पर iterates तो यह कोड O (n ^ 2) समय जटिलता है
बोरिस

मूल्यों के रूप में शब्दकोशों के साथ काम नहीं करता है
रोको मिजिक

22

यदि आप नेस्ले डिस्क्राइब्ड JSON ऑब्जेक्ट जैसे डिक्शनरी पर काम कर रहे हैं तो अन्य उत्तर काम नहीं करेंगे। इस मामले के लिए आप उपयोग कर सकते हैं:

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

यह मूल सूची के आदेश को संरक्षित करता है और यह भी एक धोखेबाज़ एल्गोरिथ्म पर वापस गिरने से शब्दकोशों जैसे अस्वाभाविक वस्तुओं को संभाल सकता है ( 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दृष्टिकोण (बिना छांटे) का उपयोग नहीं करना चाहिए क्योंकि समान शब्दकोशों के लिए आवश्यक रूप से समान आदेश नहीं है (यहां तक ​​कि पायथन 3.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)stpk के दृष्टिकोण के साथ 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फंक्शन को छोड़कर टाइमिंग में बहुत बदलाव नहीं होता है , जो इस मामले में सबसे तेज समाधान है। हालाँकि यह उस समारोह के लिए केवल सबसे अच्छा मामला है (इसलिए प्रतिनिधि नहीं है) क्योंकि यह अनौपचारिक है क्योंकि यह रनटाइम सूची में अद्वितीय मानों की मात्रा पर निर्भर करता है: 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

यहां एक त्वरित एक-लाइन समाधान के साथ एक डबल-नेस्टेड सूची समझ (@Emmanuel समाधान पर आधारित) है।

यह 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में अद्वितीय डीकट्स होंगे।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.