एक शब्दकोश से कई कुंजियों को सुरक्षित रूप से निकालना


127

मैं अपने शब्दकोश से एक प्रविष्टि, 'कुंजी' को निकालना जानता हूं d, सुरक्षित रूप से, आप करते हैं:

if d.has_key('key'):
    del d['key']

हालाँकि, मुझे शब्दकोश से सुरक्षित रूप से कई प्रविष्टियाँ निकालने की आवश्यकता है। मैं एक टपल में प्रविष्टियों को परिभाषित करने के बारे में सोच रहा था क्योंकि मुझे इसे एक से अधिक बार करने की आवश्यकता होगी।

entitiesToREmove = ('a', 'b', 'c')
for x in entitiesToRemove:
    if d.has_key(x):
        del d[x]

हालांकि, मैं सोच रहा था कि क्या ऐसा करने के लिए एक स्मार्ट तरीका है?


3
एक शब्दकोश से पुनर्प्राप्ति का समय हैशिंग के कारण लगभग O (1) है। जब तक आप प्रविष्टियों का एक महत्वपूर्ण अनुपात नहीं निकाल रहे हैं, मुझे नहीं लगता कि आप बहुत बेहतर करेंगे।
ncmathsadist

1
@ मट्टबॉर्नस्की का जवाब अधिक विहित, और भी रसीला लगता है।
आयोनिस फिलिपिपिडिस

2
StackOverflow key in dहैथ बोली जाती है: d.has_key(key) stackoverflow.com/questions/1323410/has-key-or-in
Michael Scheper

यदि आप थोड़ी मेमोरी को खाली कर सकते हैं, तो आप कर सकते हैं for x in set(d) & entities_to_remove: del d[x]। यह संभवतः entities_to_remove"अधिक" होने पर ही अधिक कुशल होगा ।
DylanYoung

जवाबों:


56

ऐसा क्यों नहीं है:

entries = ('a', 'b', 'c')
the_dict = {'b': 'foo'}

def entries_to_remove(entries, the_dict):
    for key in entries:
        if key in the_dict:
            del the_dict[key]

एक अधिक कॉम्पैक्ट संस्करण का उपयोग कर mattbornski द्वारा प्रदान किया गया dict.pop ()


14
एक खोज इंजन से आने वाले लोगों के लिए इसे जोड़ना। यदि कुंजियों को जाना जाता है (जब सुरक्षा कोई समस्या नहीं है), तो एकाधिक कुंजियों को एक पंक्ति में इस तरह से हटाया जा सकता हैdel dict['key1'], dict['key2'], dict['key3']
तीर्थ आर

आपके द्वारा हटाए जा रहे कुंजी की संख्या के आधार पर for key in set(the_dict) & entries:, key in dictपरीक्षण का उपयोग करना और बायपास करना अधिक कुशल हो सकता है ।
DylanYoung

236
d = {'some':'data'}
entriesToRemove = ('any', 'iterable')
for k in entriesToRemove:
    d.pop(k, None)

37
यह। यह चतुर पाइथोनिस्टा की पसंद है। dict.pop()प्रमुख अस्तित्व परीक्षण की आवश्यकता को समाप्त करता है। अति उत्कृष्ट।
सेसिल करी

4
इसके लायक क्या है, मुझे लगता .pop()है कि यह बुरा और अप्रासंगिक है, और इस पर स्वीकृत उत्तर को प्राथमिकता देगा।
अर्ने

5
लोगों की एक चौंका देने वाली संख्या इससे अप्रभावित दिखाई देती है :) मैं व्यक्तिगत रूप से अस्तित्व की जाँच के लिए अतिरिक्त लाइन को बुरा नहीं मानता, और यह काफी अधिक पठनीय है जब तक कि आप पहले से ही पॉप () के बारे में नहीं जानते। दूसरी ओर अगर आप इसे समझने की कोशिश कर रहे हैं या इनलाइन लैम्ब्डा यह ट्रिक एक बड़ी मदद हो सकती है। मैं यह भी कहूंगा कि यह महत्वपूर्ण है, मेरी राय में, लोगों से मिलने के लिए जहां वे हैं। मुझे यकीन नहीं है कि "बुरा और अनहेल्थोनिक" उन लोगों को देने जा रहा है जो इन उत्तरों को पढ़ रहे हैं वे व्यावहारिक मार्गदर्शन हैं जिनकी वे तलाश कर रहे हैं।
मैटबोर्नस्की

5
इसका उपयोग करने के लिए एक विशेष रूप से अच्छा कारण है। अतिरिक्त रेखा जोड़ने पर "पठनीयता" या "स्पष्टता" में सुधार हो सकता है, यह शब्दकोश में एक अतिरिक्त खोज भी जोड़ता है। यह विधि हटाने के बराबर है setdefault। यदि सही तरीके से लागू किया गया है (और मुझे यकीन है कि यह है), यह केवल dictदो के बजाय हैश-मैप में एक ही खोज करता है ।
मैड फिजिसिस्ट

2
व्यक्तिगत रूप से मैं पहले शुद्धता और स्थिरता से चिंतित होता हूं, और केवल तभी गति देता हूं जब यह अपर्याप्त रूप से तेज साबित हो। जब अनुप्रयोग स्तर पर ज़ूम आउट किया जाता है, तो इन परिचालनों के बीच गति अंतर तुच्छ होने वाला है। यह मामला हो सकता है कि एक तेज है, लेकिन मुझे उम्मीद है कि वास्तविक दुनिया में उपयोग में आप न तो ध्यान देंगे और न ही देखभाल करेंगे, और यदि आप नोटिस और देखभाल करते हैं, तो आप पायथन की तुलना में कुछ अधिक बेहतर प्रदर्शन में फिर से लिखे जाएंगे।
मैटबोर्नस्की

89

डिक्ट समझ का उपयोग करना

final_dict = {key: t[key] for key in t if key not in [key1, key2]}

जहां की 1 और की 2 को हटाया जाना है।

नीचे दिए गए उदाहरण में, चाबियाँ "बी" और "सी" को हटा दिया जाना है और इसे एक कुंजी सूची में रखा गया है।

>>> a
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
>>> keys = ["b", "c"]
>>> print {key: a[key] for key in a if key not in keys}
{'a': 1, 'd': 4}
>>> 

4
नया शब्दकोष? सूची की समझ? आपको प्रश्न पूछने वाले व्यक्ति के उत्तर को समायोजित करना चाहिए;)
ग्लासो

6
इस समाधान में एक गंभीर प्रदर्शन हिट होता है जब कार्यक्रम में आगे उपयोग करने वाले चर का उपयोग होता है। दूसरे शब्दों में, एक तानाशाही जिसमें से कुंजी को हटा दिया गया है, बनाए रखी गई वस्तुओं के साथ नए बनाए गए तानाशाह की तुलना में बहुत अधिक कुशल है।
अपालाला

14
पठनीयता के लिए, मेरा सुझाव है कि {k: v for k, v in t.items () अगर k में [key1, key2]} नहीं है
Bazin

8
जब प्रदर्शनों की सूची बहुत बड़ी हो जाती है, तो यह प्रदर्शन की समस्या भी होती है, जैसा कि खोज में होता है O(n)। पूरा ऑपरेशन है O(mn), जहां सूची में कुंजियों की संख्या mऔर nसूची में कुंजियों की संख्या है। मेरा सुझाव है कि {key1, key2}यदि संभव हो तो इसके बजाय एक सेट का उपयोग करें ।
ldavid

4
अपाला को: क्या आप मुझे यह समझने में मदद कर सकते हैं कि प्रदर्शन में कमी क्यों है?
सीन

21

एक समाधान का उपयोग कर रहा है mapऔर filterकार्य करता है

अजगर २

d={"a":1,"b":2,"c":3}
l=("a","b","d")
map(d.__delitem__, filter(d.__contains__,l))
print(d)

अजगर ३

d={"a":1,"b":2,"c":3}
l=("a","b","d")
list(map(d.__delitem__, filter(d.__contains__,l)))
print(d)

आपको मिला:

{'c': 3}

यह मेरे लिए अजगर 3.4 के साथ काम नहीं करता है:>>> d={"a":1,"b":2,"c":3} >>> l=("a","b","d") >>> map(d.__delitem__, filter(d.__contains__,l)) <map object at 0x10579b9e8> >>> print(d) {'a': 1, 'b': 2, 'c': 3}
रिसादिंह

@ रिसादिहा list(map(d.__delitem__,filter(d.__contains__,l))).... अजगर 3.4 मैप फंक्शन में एक पुनरावृत्ती लौटाता है
जोस रिकार्डो बस्टोस एम।

4
या deque(map(...), maxlen=0)कोई भी मूल्य की सूची बनाने से बचने के लिए; पहले आयात के साथfrom collections import deque
जेसन

19

यदि आपको उन कुंजियों के लिए मान प्राप्त करने की आवश्यकता है जिन्हें आप हटा रहे हैं, तो यह करने के लिए एक बहुत अच्छा तरीका होगा:

valuesRemoved = [d.pop(k, None) for k in entitiesToRemove]

आप निश्चित रूप से अभी भी कुंजी से हटाने के लिए ऐसा कर सकते हैं d, लेकिन आप अनावश्यक रूप से सूची की समझ के साथ मूल्यों की सूची बना रहे होंगे। यह केवल फ़ंक्शन के साइड इफेक्ट के लिए एक सूची समझ का उपयोग करने के लिए थोड़ा अस्पष्ट भी है।


3
या यदि आप हटाए गए प्रविष्टियों को एक शब्दकोश के रूप में रखना चाहते हैं :valuesRemoved = dict((k, d.pop(k, None)) for k in entitiesToRemove) और इसी तरह।
किंडल

आप एक चर को असाइनमेंट छोड़ सकते हैं। इस या इस तरह से यह सबसे छोटा और सबसे अजगर समाधान है और इसे कोरो उत्तर IMHO के रूप में चिह्नित किया जाना चाहिए।
गेरहार्ड हैगर

12

के साथ एक समाधान मिला popऔरmap

d = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'b', 'c']
list(map(d.pop, keys))
print(d)

इस का उत्पादन:

{'d': 'valueD'}

मैंने इस प्रश्न का उत्तर इतनी देर से दिया है क्योंकि मुझे लगता है कि यह भविष्य में मदद करेगा यदि कोई भी समान खोज करता है। और यह मदद कर सकता है।

अपडेट करें

उपरोक्त कोड एक त्रुटि फेंक देगा यदि एक कुंजी तानाशाह में मौजूद नहीं है।

DICTIONARY = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'l', 'c']

def remove_keys(key):
    try:
        DICTIONARY.pop(key, None)
    except:
        pass  # or do any action

list(map(remove_key, keys))
print(DICTIONARY)

उत्पादन:

DICTIONARY = {'b': 'valueB', 'd': 'valueD'}

1
यदि कोई कुंजी keysमौजूद नहीं है, तो यह उत्तर एक अपवाद को फेंक देगा d- आपको पहले फ़िल्टर करना होगा।
इंगोफ्रीयर

@ingofreyer ने अपवाद हैंडलिंग के लिए कोड अपडेट किया। इस मुद्दे को खोजने के लिए धन्यवाद। मुझे लगता है कि अब यह काम करेगा। :)
शुभम श्रीवास्तव

धन्यवाद, इससे सभी को यह उत्तर खोजने में मदद मिलनी चाहिए :-)
ingofreyer

मानचित्र का उपयोग करके उत्पाद के रूप में एक सूची बनाना, यह काफी धीमा बनाता है, यह वास्तव में इस पर लूप करना बेहतर है।
चार्ली क्लार्क

4

मुझे किसी भी मौजूदा जवाब से कोई समस्या नहीं है, लेकिन मैं इस समाधान को नहीं पाकर हैरान था:

keys_to_remove = ['a', 'b', 'c']
my_dict = {k: v for k, v in zip("a b c d e f g".split(' '), [0, 1, 2, 3, 4, 5, 6])}

for k in keys_to_remove:
    try:
        del my_dict[k]
    except KeyError:
        pass

assert my_dict == {'d': 3, 'e': 4, 'f': 5, 'g': 6}

नोट: मैं यहाँ से आ रहे इस सवाल से लड़खड़ा गया । और मेरा जवाब इस जवाब से संबंधित है ।


3

क्यों नहीं:

entriestoremove = (2,5,1)
for e in entriestoremove:
    if d.has_key(e):
        del d[e]

मुझे नहीं पता कि आपके "स्मार्ट तरीके" से क्या मतलब है। निश्चित रूप से अन्य तरीके हैं, शायद शब्दकोश की समझ के साथ:

entriestoremove = (2,5,1)
newdict = {x for x in d if x not in entriestoremove}

2

पंक्ति में

import functools

#: not key(c) in d
d = {"a": "avalue", "b": "bvalue", "d": "dvalue"}

entitiesToREmove = ('a', 'b', 'c')

#: python2
map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove)

#: python3

list(map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove))

print(d)
# output: {'d': 'dvalue'}

2

Cpython 3 के लिए कुछ समय परीक्षण से पता चलता है कि लूप के लिए एक सरल सबसे तेज़ तरीका है, और यह काफी पठनीय है। किसी फ़ंक्शन में जोड़ने से बहुत अधिक ओवरहेड नहीं होता है:

समय परिणाम (10k पुनरावृत्तियों):

  • all(x.pop(v) for v in r) # 0.85
  • all(map(x.pop, r)) # 0.60
  • list(map(x.pop, r)) # 0.70
  • all(map(x.__delitem__, r)) # 0.44
  • del_all(x, r) # 0.40
  • <inline for loop>(x, r) # 0.35
def del_all(mapping, to_remove):
      """Remove list of elements from mapping."""
      for key in to_remove:
          del mapping[key]

छोटे पुनरावृत्तियों के लिए, फ़ंक्शन कॉल के ओवरहेड होने के कारण, उस 'इनलाइन' को थोड़ा तेज किया गया था। लेकिन del_allलिंट-सेफ, पुन: प्रयोज्य और सभी अजगर समझ और मानचित्रण निर्माणों की तुलना में तेज़ है।


0

मुझे लगता है कि इस तथ्य का उपयोग करते हुए कि कुंजी को एक सेट के रूप में माना जा सकता है यदि आप अजगर 3 पर हैं तो सबसे अच्छा तरीका है:

def remove_keys(d, keys):
    to_remove = set(keys)
    filtered_keys = d.keys() - to_remove
    filtered_values = map(d.get, filtered_keys)
    return dict(zip(filtered_keys, filtered_values))

उदाहरण:

>>> remove_keys({'k1': 1, 'k3': 3}, ['k1', 'k2'])
{'k3': 3}

0

शब्दकोशों के लिए सेट विधियों के लिए पूर्ण समर्थन करना अच्छा होगा (और हम Python 3.9 के साथ मिल रही अपवित्र गंदगी नहीं) ताकि आप बस कुंजी का एक सेट "हटा" सकें। हालाँकि, जब तक ऐसा नहीं होता है, और आपके पास एक बड़ी डिक्शनरी है जिसमें संभावित रूप से हटाने के लिए बड़ी संख्या में कुंजियाँ हैं, आप प्रदर्शन के बारे में जानना चाह सकते हैं। तो, मैंने कुछ कोड बनाए हैं जो सार्थक तुलना के लिए कुछ बड़ा बनाता है: एक 100,000 x 1000 मैट्रिक्स, इसलिए कुल 10,000,00 आइटम।

from itertools import product
from time import perf_counter

# make a complete worksheet 100000 * 1000
start = perf_counter()
prod = product(range(1, 100000), range(1, 1000))
cells = {(x,y):x for x,y in prod}
print(len(cells))

print(f"Create time {perf_counter()-start:.2f}s")
clock = perf_counter()
# remove everything above row 50,000

keys = product(range(50000, 100000), range(1, 100))

# for x,y in keys:
#     del cells[x, y]

for n in map(cells.pop, keys):
    pass

print(len(cells))
stop = perf_counter()
print(f"Removal time {stop-clock:.2f}s")

कुछ सेटिंग्स में 10 मिलियन आइटम या अधिक असामान्य नहीं है। अपने स्थानीय मशीन पर दो तरीकों की तुलना करने पर मुझे उपयोग करते समय थोड़ा सुधार दिखाई देता है mapऔर popसंभवत: कम फ़ंक्शन कॉल के कारण, लेकिन दोनों मेरी मशीन पर लगभग 2.5 s लेते हैं। लेकिन यह पहली जगह (55s) में शब्दकोश बनाने के लिए आवश्यक समय की तुलना में या लूप के भीतर चेक सहित शामिल है। यदि यह संभावना है, तो एक सेट बनाने के लिए सबसे अच्छा है जो शब्दकोश कुंजी और आपके फ़िल्टर का एक चौराहा है:

keys = cells.keys() & keys

संक्षेप में: delपहले से ही बहुत अधिक अनुकूलित है, इसलिए इसका उपयोग करने के बारे में चिंता न करें।


-1

मुझे इस चर्चा में देर है लेकिन किसी और के लिए। एक समाधान कुंजी की एक सूची बनाने के लिए हो सकता है।

k = ['a','b','c','d']

फिर एक सूची में पॉप () का उपयोग करें, या लूप के लिए, चाबियों पर पुनरावृति करने के लिए और एक समय में पॉप एक जैसे।

new_dictionary = [dictionary.pop(x, 'n/a') for x in k]

कुंजी मौजूद नहीं होने की स्थिति में 'n / a' है, एक डिफ़ॉल्ट मान को वापस करने की आवश्यकता है।


8
new_dictionaryएक सूची की तरह एक बहुत भयानक लग रहा है;)
DylanYoung
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.