How do I delete items from a dictionary while iterating over it?
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}
Remove element from dictionary by key while iterating
Here is a version modifying your first example, you will need to "copy" your list to iterate with it and deleting at the same time. After you're iterating with the copied list, you can delete from the original list as necessary.
import copy
qr = [{'A': 'B', 'C': '3', 'EE': None, 'P': '343', 'AD': ' ', 'B': ''},
{'A': 'B', 'C': '3', 'EE': None, 'P': '343', 'AD': ' ', 'B': ''}]
for i, row in enumerate(copy.deepcopy(qr)):
for key, value in row.items():
if value in {' ', None, ''}:
del qr[i][key]
print(qr)
Other than that, usually you want to create a new list than to delete from the original list. A simple list comprehension will do the trick:
qr = [{k:v for k, v in row.items() if v not in {' ', None, ''}} for row in qr]
print(qr) # same result
Output for both:
[{'A': 'B', 'C': '3', 'P': '343'},
{'A': 'B', 'C': '3', 'P': '343'}]
Deleting items from a dictionary with a for loop
Due to the fact that Python dictionaries are implemented as hash tables, you shouldn't rely on them having any sort of an order. Key order may change unpredictably (but only after insertion or removal of a key). Thus, it's impossible to predict the next key. Python throws the RuntimeError
to be safe, and to prevent people from running into unexpected results.
Python 2's dict.items
method returns a copy of key-value pairs, so you can safely iterate over it and delete values you don't need by keys, as @wim suggested in comments. Example:
for k, v in my_dict.items():
if v < threshold_value:
del my_dict[k]
However, Python 3's dict.items
returns a view object that reflects all changes made to the dictionary. This is the reason the solution above only works in Python 2. You may convert my_dict.items()
to list
(tuple
etc.) to make it Python 3-compatible.
Another way to approach the problem is to select keys you want to delete and then delete them
keys = [k for k, v in my_dict.items() if v < threshold_value]
for x in keys:
del my_dict[x]
This works in both Python 2 and Python 3.
delete item from dictionary while iterating over it
The use of ElementAt(i)
is not the ideal way to get a particular item and will perform poorly. Its usage suggests that you want a collection with an indexer, such as IList<T>
.
Using your current setup, you could use this approach:
foreach(var key in buzzCompaignsPerUserIntersets.Keys)
{
var list = buzzCompaignsPerUserIntersets[key];
var query = list.Where(o => o.MayaMembership
.MayaProfile.MayaProfileId == profile_id)
.ToArray();
foreach (var item in query)
{
list.Remove(item);
}
}
Alternately, if you can change the ICollection<T>
to an IList<T>
you could use the indexer and the RemoveAt
method. That would look like this:
foreach(var key in buzzCompaignsPerUserIntersets.Keys)
{
var list = buzzCompaignsPerUserIntersets[key];
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i].MayaMembership.MayaProfile.MayaProfileId == profile_id)
{
list.RemoveAt(i);
}
}
}
A List<T>
would let you use the RemoveAll
method. If you're interested in how that works take a look at my answer to another question.
custom dict that allows delete during iteration
As you note, you can store the items to delete somewhere and defer the deletion of them until later. The problem then becomes when to purge them and how to make sure that the purge method eventually gets called. The answer to this is a context manager which is also a subclass of dict
.
class dd_dict(dict): # the dd is for "deferred delete"
_deletes = None
def __delitem__(self, key):
if key not in self:
raise KeyError(str(key))
dict.__delitem__(self, key) if self._deletes is None else self._deletes.add(key)
def __enter__(self):
self._deletes = set()
def __exit__(self, type, value, tb):
for key in self._deletes:
try:
dict.__delitem__(self, key)
except KeyError:
pass
self._deletes = None
Usage:
# make the dict and do whatever to it
ddd = dd_dict(a=1, b=2, c=3)
# now iterate over it, deferring deletes
with ddd:
for k, v in ddd.iteritems():
if k is "a":
del ddd[k]
print ddd # shows that "a" is still there
print ddd # shows that "a" has been deleted
If you're not in a with
block, of course, deletes are immediate; as this is a dict
subclass, it works just like a regular dict
outside of a context manager.
You could also implement this as a wrapper class for a dictionary:
class deferring_delete(object):
def __init__(self, d):
self._dict = d
def __enter__(self):
self._deletes = set()
return self
def __exit__(self, type, value, tb):
for key in self._deletes:
try:
del self._dict[key]
except KeyError:
pass
del self._deletes
def __delitem__(self, key):
if key not in self._dict:
raise KeyError(str(key))
self._deletes.add(key)
d = dict(a=1, b=2, c=3)
with deferring_delete(d) as dd:
for k, v in d.iteritems():
if k is "a":
del dd[k] # delete through wrapper
print d
It's even possible to make the wrapper class fully functional as a dictionary, if you want, though that's a fair bit more code.
Performance-wise, this is admittedly not such a win, but I like it from a programmer-friendliness standpoint. The second method should be very slightly faster since it's not testing a flag on each delete.
Deleting while iterating over a dictionary
Keep a list of the keys you wish to remove as you find them. Then, when you are done, iterate over this list, calling myDictionary.Remove(key)
on each key you stored.
delete items from a set while iterating over it
First, using a set, as Zero Piraeus told us, you can
myset = set([3,4,5,6,2])
while myset:
myset.pop()
print(myset)
I added a print
method giving these outputs
>>>
set([3, 4, 5, 6])
set([4, 5, 6])
set([5, 6])
set([6])
set([])
If you want to stick to your choice for a list, I suggest you deep copy the list using a list comprehension, and loop over the copy, while removing items from original list. In my example, I make length of original list decrease at each loop.
l = list(myset)
l_copy = [x for x in l]
for k in l_copy:
l = l[1:]
print(l)
gives
>>>
[3, 4, 5, 6]
[4, 5, 6]
[5, 6]
[6]
[]
How to remove items from a list while iterating?
You can use a list comprehension to create a new list containing only the elements you don't want to remove:
somelist = [x for x in somelist if not determine(x)]
Or, by assigning to the slice somelist[:]
, you can mutate the existing list to contain only the items you want:
somelist[:] = [x for x in somelist if not determine(x)]
This approach could be useful if there are other references to somelist
that need to reflect the changes.
Instead of a comprehension, you could also use itertools
. In Python 2:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
Or in Python 3:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
Related Topics
Python Subprocess Get Children's Output to File and Terminal
Efficiently Using Multiple Numpy Slices for Random Image Cropping
Typeerror: Can't Convert 'Int' Object to Str Implicitly
How to Jump to a Particular Line in a Huge Text File
How to Reset Index in a Pandas Dataframe
Pandas Long to Wide Reshape, by Two Variables
How to Correctly Clean Up a Python Object
How to Extract Multiple JSON Objects from One File
How to Capture Stdout Output from a Python Function Call
Shooting a Bullet in Pygame in the Direction of Mouse
How to Upgrade All Python Packages with Pip
Create Pandas Dataframe from Txt File with Specific Pattern
How to Read a Text File into a List or an Array with Python
Opencv 2.4 Videocapture Not Working on Windows
Determine Function Name from Within That Function (Without Using Traceback)