Update dictionary during iteration

Question:

Is it possible to iterate through a dictionary while updating a separate dictionary? I tried to create a copy of my original dictionary and edit that one but I’m still getting an error

d = {30:3, 54:5, 16:2}
r = d
for k,v in d.items():
    biggest = max(d,key = d.get)
    del(r[biggest])

I need to find the largest value of the dictionary each time it loops through. Then I need to remove that item from the dictionary so I can find the next largest item.

The error I get when I run this code is as follows.

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    for k,v in d.items():
RuntimeError: dictionary changed size during iteration
Asked By: DakotaDickey44

||

Answers:

Iterate on a copy of the dict:

d = {30:3, 54:5, 16:2}
r = d
for k,v in dict(d).items():
    biggest = max(d,key = d.get)
    del(r[biggest])
Answered By: Pierre Barre

The problem is that when you use = in the line r = d, then r is not a new object. It is the same d. I mean they are refer to a single dictionary:

>>> x = {'a':1, 'b':2}
>>> y = x
>>> x
{'a': 1, 'b': 2}
>>> y
{'a': 1, 'b': 2}
>>> x is y
True

So if you change one of them, the other will change too:

>>> y['c']=3
>>> y
{'a': 1, 'c': 3, 'b': 2}
>>> x
{'a': 1, 'c': 3, 'b': 2}

Using id() method you can check if they are referring to different places in memory or not:

>>> id(y)
44703816L
>>> id(x)
44703816L
>>> 

So, you need to use copy() method instead of =:

>>> import copy
>>> z = copy.copy(x)
>>> z
{'a': 1, 'c': 3, 'b': 2}
>>> x
{'a': 1, 'c': 3, 'b': 2}
>>> z is x
False
>>> 

That cause changing one of them, don’t change the other:

>>> z
{'a': 1, 'c': 3, 'b': 2}
>>> x
{'a': 1, 'c': 3, 'b': 2}
>>> z['d']=4
>>> z
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
>>> x
{'a': 1, 'c': 3, 'b': 2}
>>> 
Answered By: EbraHim

As others have pointed out, you cannot change the size of the dictionary while iterating thorough it. It has also been noted by @user312016 that you can iterate over a copy and modify the original.

I am not sure what the intention is, but this code will sort the items from largest value to smallest so you don’t have to find the max on each iteration:

d = {30:3, 54:5, 16:2}
d_ = sorted(d.items(), key=lambda x: x[1], reverse=True)

for k, v in d_:
    print(k, v)

54, 5
30, 3
16, 2
Answered By: sberry

You are actually iterating over the same dictionary since r = d does not creates a new dictionary. r is just another reference to the same dictionary. You can check the object identities to confirm that:

>>> r = d
>>> r is d
True

Please, see this discussion for more details about object identity:

"is" operator behaves unexpectedly with integers

So, the right thing to do is first create a copy of the dictionary and then alter it:

>>> r = d.copy()
>>> r is d
False

And the iteration:

for k,v in d.items():
    biggest = max(d,key = d.get)
    del(r[biggest])

So, from your code we just need to change a single line:

d = {30:3, 54:5, 16:2}
r = d.copy() # changed: use copy here
for k,v in d.items():
    biggest = max(d,key = d.get)
    del(r[biggest])
Answered By: marcospereira
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.