Reputation:
Can I delete items from a dictionary in Python while iterating over it?
I want to remove elements that don't meet a certain condition from the dictionary, instead of creating an entirely new dictionary. Is the following a good solution, or are there better ways?
for k, v in mydict.items():
if k == val:
del mydict[k]
Upvotes: 460
Views: 287852
Reputation: 1260
list(mydict)
can be used:
>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
... if k == 'three':
... del mydict[k]
...
>>> mydict
{'four': 4, 'two': 2, 'one': 1}
This corresponds to a parallel structure for lists:
>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist): # or mylist[:]
... if k == 'three':
... mylist.remove(k)
...
>>> mylist
['one', 'two', 'four']
Both work in python2 and python3.
Upvotes: 13
Reputation:
Another nice and efficient way using partial function
next((partial(d.pop, k) for k in d if k == v), partial(print, f"{v} key not found"))()
Upvotes: 0
Reputation: 310
One-liner:
my_dict = {k: v for k, v in my_dict.copy().items() if not k == value}
The my_dict.copy()
object is used for the iteration only and will not be available outside the scope of the dictionary comprehension. This avoids editing the object over which you are currently iterating, as advised against by @user395760 in their answer.
You can split this over multiple lines for clarity:
my_dict = {
k: v
for k, v in my_dict.copy().items()
if not k == value
}
Upvotes: 1
Reputation: 15818
For Python 3+:
>>> mydict
{'four': 4, 'three': 3, 'one': 1}
>>> for k in list(mydict.keys()):
... if mydict[k] == 3:
... del mydict[k]
>>> mydict
{'four': 4, 'one': 1}
The other answers work fine with Python 2 but raise a RuntimeError
for Python 3:
RuntimeError: dictionary changed size during iteration.
This happens because mydict.keys()
returns an iterator not a list.
As pointed out in comments simply convert mydict.keys()
to a list by list(mydict.keys())
and it should work.
For Python 2:
A simple test in the console shows you cannot modify a dictionary while iterating over it:
>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
... if k == 'two':
... del mydict[k]
------------------------------------------------------------
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
As stated in delnan's answer, deleting entries causes problems when the iterator tries to move onto the next entry. Instead, use the keys()
method to get a list of the keys and work with that:
>>> for k in mydict.keys():
... if k == 'two':
... del mydict[k]
>>> mydict
{'four': 4, 'three': 3, 'one': 1}
If you need to delete based on the items value, use the items()
method instead:
>>> for k, v in mydict.items():
... if v == 3:
... del mydict[k]
>>> mydict
{'four': 4, 'one': 1}
Upvotes: 457
Reputation: 1246
With python3, iterate on dic.keys() will raise the dictionary size error. You can use this alternative way:
Tested with python3, it works fine and the Error "dictionary changed size during iteration" is not raised:
my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )
# Iterate on the list:
for k in key_list:
print(key_list)
print(my_dic)
del( my_dic[k] )
print( my_dic )
# {}
Upvotes: 12
Reputation: 27062
There is a way that may be suitable if the items you want to delete are always at the "beginning" of the dict iteration
while mydict:
key, value = next(iter(mydict.items()))
if should_delete(key, value):
del mydict[key]
else:
break
The "beginning" is only guaranteed to be consistent for certain Python versions/implementations. For example from What’s New In Python 3.7
the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.
This way avoids a copy of the dict that a lot of the other answers suggest, at least in Python 3.
Upvotes: 3
Reputation: 1442
I tried the above solutions in Python3 but this one seems to be the only one working for me when storing objects in a dict. Basically you make a copy of your dict() and iterate over that while deleting the entries in your original dictionary.
tmpDict = realDict.copy()
for key, value in tmpDict.items():
if value:
del(realDict[key])
Upvotes: 2
Reputation: 799310
Iterate over a copy instead, such as the one returned by items()
:
for k, v in list(mydict.items()):
Upvotes: 36
Reputation: 151
You can use a dictionary comprehension.
d = {k:d[k] for k in d if d[k] != val}
Upvotes: 15
Reputation: 41
You could first build a list of keys to delete, and then iterate over that list deleting them.
dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
if v%2 == 1:
delete.append(k)
for i in delete:
del dict[i]
Upvotes: 4
Reputation: 107736
You could also do it in two steps:
remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]
My favorite approach is usually to just make a new dict:
# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)
Upvotes: 119
Reputation:
You can't modify a collection while iterating it. That way lies madness - most notably, if you were allowed to delete and deleted the current item, the iterator would have to move on (+1) and the next call to next
would take you beyond that (+2), so you'd end up skipping one element (the one right behind the one you deleted). You have two options:
.keys()
et al for this (in Python 3, pass the resulting iterator to list
). Could be highly wasteful space-wise though.mydict
as usual, saving the keys to delete in a seperate collection to_delete
. When you're done iterating mydict
, delete all items in to_delete
from mydict
. Saves some (depending on how many keys are deleted and how many stay) space over the first approach, but also requires a few more lines.Upvotes: 27