Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deleting items from a dictionary with a for loop [duplicate]

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]

print(my_dict)

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...

like image 840
Optimesh Avatar asked May 11 '14 15:05

Optimesh


1 Answers

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.

like image 175
vaultah Avatar answered Sep 25 '22 14:09

vaultah