辞書から複数のキーを安全に削除する


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
@mattbornskiの答えは、より標準的で、簡潔でもあります。
Ioannis Filippidis 2015

2
StackOverflowが話されています:stackoverflow.com/questions/1323410/has-key-or-inkey in dよりもPythonicですd.has_key(key)
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]

よりコンパクトなバージョンがdict.pop()を使用してmattbornskiによって提供されました


14
検索エンジンから来た人のためにこれを追加します。キーがわかっている場合(安全が問題ではない場合)、このように1行で複数のキーを削除できますdel dict['key1'], dict['key2'], dict['key3']
Tirtha R

削除するキーの数によっては、テストを使用for key in set(the_dict) & entries:してバイパスする方が効率的key in dictです。
DylanYoung

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

37
この。これはPythonistaの賢い選択です。dict.pop()重要な存在テストの必要性を排除します。優れた。
Cecil Curry

4
それが価値があるものについて.pop()は、私は悪いと非Pythonicであり、これよりも受け入れられた答えを好むと思います。
Arne

5
驚異的な数の人がこれに悩まされていないように見えます:)個人的に存在を確認するための余分な行を気にしません。また、pop()についてすでに知っている場合を除いて、はるかに読みやすくなっています。一方、理解力またはインラインラムダでこれを実行しようとした場合、このトリックは大きな助けになるでしょう。私の意見では、彼らがいる場所の人々に会うことも重要だと私は言います。これらの答えを読んでいる人たちが「悪い、非Pythonic」であることが、彼らが探している実際的なガイダンスを提供することになるかどうかはわかりません。
mattbornski 2018年

5
特にあり良いこれを使用する理由は。追加の行を追加すると、「読みやすさ」または「明快さ」が向上する可能性がありますが、辞書への検索も追加されます。このメソッドは、を実行するのと同等の削除setdefaultです。正しく実装されていれば(そうだと私は確信しています)、ハッシュマップへのルックアップは、dict2つではなく、1つだけです。
マッド

2
個人的には、正確さと保守性を最初に気にし、速度が不十分であることが証明された場合にのみ速度を気にします。これらの操作の速度の違いは、アプリケーションレベルにズームアウトした場合、ごくわずかです。それはより速いケースかもしれませんが、私は実際の使用では気付かないし気にしないだろうと私は期待しています。
mattbornski

89

口語読解の使用

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

どこKEY1KEY2は削除されます。

以下の例では、キー「b」と「c」が削除され、キーリストに保持されます。

>>> 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
新しい辞書?リスト内包?質問をする人の答えを調整する必要があります;)
Glaslos

6
このソリューションは、を保持する変数がプログラムでさらに使用される場合、パフォーマンスに重大な影響を与えます。つまり、キーが削除された辞書は、アイテムが保持された状態で新しく作成された辞書よりもはるかに効率的です。
アパララ2013年

14
読みやすくするために、{k:v for k.v in t.items()if not k in [key1、key2]}
Frederic Bazin

8
キーのリストが大きすぎる場合、検索に時間がかかるため、これにはパフォーマンスの問題もありますO(n)。操作全体はですO(mn)。ここmで、は辞書内のキーnの数とリスト内のキーの数です。{key1, key2}可能であれば、代わりにセットを使用することをお勧めします。
ldavid 2015年

4
Apalalaへ:パフォーマンスに影響がある理由を理解していただけますか?
Sean

21

ソリューションが使用mapしているfilter機能

python 2

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

python 3

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

あなたが得る:

{'c': 3}

これはpython 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}
Risadinha

@Risadinha list(map(d.__delitem__,filter(d.__contains__,l))).... Python 3.4のmap関数は反復子を返します
Jose Ricardo Bustos M.

4
またはdeque(map(...), maxlen=0)、None値のリストの作成を回避するため。最初のインポートfrom collections import deque
Jason

19

削除するキーの値も取得する必要がある場合、これは非常に良い方法です。

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

もちろん、からキーを削除するためだけにこれを行うこともできdますが、リスト内包表記で値のリストを不必要に作成することになります。関数の副作用のためだけにリスト内包表記を使用することも少し不明確です。


3
または、削除されたエントリを辞書として 保持したい場合valuesRemoved = dict((k, d.pop(k, None)) for k in entitiesToRemove)など。
キンダル2012年

変数への割り当ては省略できます。この方法またはその方法では、これは最も短く、最もPython的なソリューションであり、正解の答えである私見としてマークする必要があります。
Gerhard Hagerer、2015年

12

解決策を見つけたpopmap

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

これの出力:

{'d': 'valueD'}

誰かが同じものを検索することが将来的に役立つと思うので、私はこの質問にとても遅く答えました。そして、これは役立つかもしれません。

更新

上記のコードは、dictにキーが存在しない場合にエラーをスローします。

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は、例外処理のコードを更新しました。この問題を見つけてくれてありがとう。今はうまくいくと思います。:)
Shubham Srivastava

おかげで、これは誰もがこの答えを見つけるのに役立つはずです:-)
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のいくつかのタイミングテストは、単純なforループが最速の方法であり、非常に読みやすいことを示しています。関数を追加しても、オーバーヘッドはあまり発生しません。

timeitの結果(1万回の反復):

  • 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、糸くずの出ない安全で再利用可能であり、すべてのpython内包およびマッピング構成よりも高速です。


0

python 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,000x 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")

設定によっては1000万個以上のアイテムも珍しくありません。ローカルマシンで2つの方法を比較するmappop、おそらく関数呼び出しが少ないため、およびを使用すると若干の改善が見られますが、私のマシンではどちらも約2.5秒かかります。しかし、これは、最初に辞書を作成するのに必要な時間(55秒)に比べて、またはループ内にチェックを含めるのに比べると見劣りします。これが可能性が高い場合は、辞書のキーとフィルターの共通部分であるセットを作成するのが最善です。

keys = cells.keys() & keys

要約すると:delはすでに非常に最適化されているため、使用する必要はありません。


-1

私はこの議論に遅れましたが、他の誰にとってもです。解決策は、そのようなキーのリストを作成することかもしれません。

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

次に、リスト内包表記またはforループでpop()を使用して、キーを反復処理し、一度に1つずつポップします。

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

「n / a」は、キーが存在しない場合、デフォルト値を返す必要があります。


8
new_dictionaryリストのようにひどく見える;)
DylanYoung
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.