Optimesh Optimesh - 1 year ago 38
Python Question

Deleting items from a dictionary with a for loop

I'm trying to drop items from a dictionary if the value of the key is below a certain threshold. For a simple example to what I mean:

my_dict = {'blue': 1, 'red': 2, 'yellow': 3, 'green': 4}

for color in my_dict:
threshold_value = 3
if my_dict[color] < threshold_value:
del my_dict[color]


Now, I get a
RuntimeError: dictionary changed size during iteration
error. No big surprises there. The reason I'm posting this question is:

  1. Find out if there's an elegant solution that doesn't require creating a new dictionary (that holds only the keys with values >= threshold).

  2. Try to understand Python's rationale here. The way I read it to myself is: "go to the first key. Is the value of that key < x ? if yes - del this key:value item and continue on the the next key in the dictionary, if no - continue to next key without doing anything". In other words, what happened historically to previous keys shouldn't affect where I go next. I'm looking forward to the next items, regardless of the past.
    I know it's a funny (some might say stupid, I'll give you that) but what's Python's "way of thinking" about this loop? Why doesn't it work? How would Python read it out loud to itself? Just trying to get a better understanding of the language...

Answer Source

Due to the fact that Python dictionaries are implemented as hash tables, you shouldn't rely on them having some definite order. Key order may change unpredictably (but only after insertion or removal of a key).

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 supports both Python 2 and Python 3.