44

I wonder if there is simple way to remove one or more dictionary element(s) from a python dictionary by value.

We have a dictionary called myDict:

myDict = {1:"egg", "Answer":42, 8:14, "foo":42}

and want to remove all items which values are equal to 42.

Implementation suggestion:

  1. Get a list of all keys of a certain value in myDict
    (See for instance get key by value in dictionary.)

  2. Delete this dict element or elements (based on the found keys) from myDict
    (For more information, see Delete an element from a dictionary.)

So, what do you think now is the most elegant and most “pythonic” way to implement this problem in Python?

Community
  • 1
  • 1
elegent
  • 3,857
  • 3
  • 23
  • 36
  • The desire to "Get the _first_ ... key of a value in myDict" is a bit dubious, @elegent, as the notion of *first* doesn't really apply to dictionaries. – boardrider Mar 24 '15 at 18:27
  • @user1656850: Yes, you are absolutely right! Thank you very much for pointing that out. I updated the question :) – elegent Mar 24 '15 at 18:48

5 Answers5

74

You can use a simple dict comprehension:

myDict = {key:val for key, val in myDict.items() if val != 42}

As such:

>>> {key:val for key, val in myDict.items() if val != 42}
{8: 14, 1: 'egg'}
elegent
  • 3,857
  • 3
  • 23
  • 36
A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
  • 11
    I would not name it `item, key` and would name it `key, val` instead and use `val != 42` because it's more semantically correct. – Shashank Mar 23 '15 at 19:46
  • Thanks for your answer! A `dict`-comprehension is a nice idea, but in my opinion the question was about how to remove items form a dict. I know that we only need to assign the resulting dict of the comprehension to `myDict`: `myDict = {key:val for key, val in myDict.items() if val != 42}`, so if it is convenient to you please change your answer. – elegent Mar 24 '15 at 15:26
11

You must create a copy to iterate over as changing the size of the dictionary inside of a loop causes a RunTimeError. Iterate over key, value pairs in your dictionary copy using items() and compare each value to the value you are looking for. If they match, delete the key from the dictionary.

    for key, value in dict(myDict).items():
        if value == 42:
            del mydict[key]

Adding answer for question in the comments below as it was too big for a comment. Here is a quick console session showing that mydict.copy() and dict(myDict) accomplish the same thing.

>>>import copy
>>>dict1 = {1:"egg", "Answer":42, 8:14, "foo":42}
>>>dict2 = dict(dict1)
>>>dict3 = dict1.copy()
>>>dict4 = dict1
>>>dict1[1] = "egg sandwich"
>>>dict1
{'Answer': 42, 1: 'egg sandwich', 'foo': 42, 8: 14}
>>>dict2
{'Answer': 42, 1: 'egg', 'foo': 42, 8: 14}
>>>dict3
{'Answer': 42, 1: 'egg', 'foo': 42, 8: 14}
>>>dict4
{'Answer': 42, 1: 'egg sandwich', 'foo': 42, 8: 14}
>>>dict2['foo'] = "I pity the"
dict1
>>>{'Answer': 42, 1: 'egg sandwich', 'foo': 42, 8: 14}
>>>dict2
{'Answer': 42, 1: 'egg', 'foo': 'I pity the', 8: 14}
>>>dict3
{'Answer': 42, 1: 'egg', 'foo': 42, 8: 14}
>>>dict4
{'Answer': 42, 1: 'egg sandwich', 'foo': 42, 8: 14}
>>>dict4[8] = "new"
>>>dict1
{'Answer': 42, 1: 'egg sandwich', 'foo': 42, 8: 'new'}
>>>dict2
{'Answer': 42, 1: 'egg', 'foo': 'I pity the', 8: 14}
>>>dict3
{'Answer': 42, 1: 'egg', 'foo': 42, 8: 14}
>>>dict4
{'Answer': 42, 1: 'egg sandwich', 'foo': 42, 8: 'new'}
`
cruane
  • 111
  • 5
2

I like following a "hit list" approach where you iterate through the dictionary, then add the ones you want to delete to a list, then after iterating, delete the entries from that list like so:

hitList =[] for dictEntry: if test condition, hitList.append

for entry in hitList: delete dict[entry]

this is just some pseudocode but i've been successful with this in the past

NevDev
  • 604
  • 5
  • 8
  • I prefer this approach too, beacuse no copy has to be made. It is applicable to a list too, just store the indices and then delete the corresponding elements, but starting with the highest index first. – VPfB Oct 19 '15 at 08:16
1

You can iterate over a copy and do a lookup:

for k in myDict.copy():
    if myDict[k] == 42:
        del myDict[k]

Or only copy the keys:

myDict = {1:"egg", "Answer":42, 8:14, "foo":42}
for k in list(myDict):
    if myDict[k] == 42:
        del myDict[k]
print(myDict)
{8: 14, 1: 'egg'}

Which if you want to mutate the original dict should be the most efficient.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
0

Use a dictionary comprehension.

if you need a copy use iteritems()

>>> {k:v for k, v in myDict.iteritems() if v!=42}
{8: 14, 1: 'egg'}

if you don't need a copy of your dictionary you can use viewitems()

>>> {k:v for k, v in myDict.viewitems() if v!=42}
{8: 14, 1: 'egg'}
kasper Taeymans
  • 6,950
  • 5
  • 32
  • 51
  • please leave a comment for the downvote so I can improve my answer. – kasper Taeymans Mar 23 '15 at 20:13
  • 1
    Thanks! In Python 3.x we only use `.items()` instead of `.iteritems()` and `viewitems()`? – elegent Mar 24 '15 at 15:49
  • 1
    yes there are differences in python 3.x and 2.x however your question is not tagged with a python version so I assumed you were looking for a 2.x solution. – kasper Taeymans Mar 24 '15 at 17:12
  • 1
    Yes, sorry you are right. If it is convenient to you, you could add this extra information to your answer, so we can see the difference between the two Python versions. :) – elegent Mar 24 '15 at 18:56