Ошибка dictionary changed size during iteration

Suppose I have a dictionary of lists:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

Now I want to remove key-value pairs where the values are empty lists. I tried this code:

for i in d:
    if not d[i]:
        d.pop(i)

but this gives an error:

RuntimeError: dictionary changed size during iteration

I understand that entries can’t be added or removed from a dictionary while iterating through it. How can I work around this limitation in order to solve the problem?


See Modifying a Python dict while iterating over it for citations that this can cause problems, and why.

Karl Knechtel's user avatar

Karl Knechtel

62.3k11 gold badges102 silver badges153 bronze badges

asked Aug 13, 2012 at 20:30

user1530318's user avatar

user1530318user1530318

25.6k15 gold badges37 silver badges48 bronze badges

3

In Python 3.x and 2.x you can use use list to force a copy of the keys to be made:

for i in list(d):

In Python 2.x calling .keys made a copy of the keys that you could iterate over while modifying the dict:

for i in d.keys():

but on Python 3.x, .keys returns a view object instead, so it won’t fix your error.

Boris Verkhovskiy's user avatar

answered Aug 13, 2012 at 20:33

Mark Byers's user avatar

Mark ByersMark Byers

813k193 gold badges1583 silver badges1452 bronze badges

7

You only need to use copy:

This way you iterate over the original dictionary fields and on the fly can change the desired dict d.
It works on each Python version, so it’s more clear.

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

(BTW — Generally to iterate over copy of your data structure, instead of using .copy for dictionaries or slicing [:] for lists, you can use import copy -> copy.copy (for shallow copy which is equivalent to copy that is supported by dictionaries or slicing [:] that is supported by lists) or copy.deepcopy on your data structure.)

mkrieger1's user avatar

mkrieger1

19.3k5 gold badges54 silver badges65 bronze badges

answered Mar 31, 2016 at 10:35

Alon Elharar's user avatar

1

Just use dictionary comprehension to copy the relevant items into a new dict:

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

For this in Python 2:

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

Peter Mortensen's user avatar

answered Aug 13, 2012 at 20:38

Maria Zverina's user avatar

Maria ZverinaMaria Zverina

10.9k3 gold badges44 silver badges61 bronze badges

3

This worked for me:

d = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(d.items()):
    if value == '':
        del d[key]
print(d)
# {1: 'a', 3: 'b', 6: 'c'}

Casting the dictionary items to list creates a list of its items, so you can iterate over it and avoid the RuntimeError.

wjandrea's user avatar

wjandrea

28.5k9 gold badges62 silver badges82 bronze badges

answered Jan 31, 2020 at 9:05

singrium's user avatar

singriumsingrium

2,7465 gold badges32 silver badges45 bronze badges

0

I would try to avoid inserting empty lists in the first place, but, would generally use:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

If prior to 2.7:

d = dict( (k, v) for k,v in d.iteritems() if v )

or just:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

answered Aug 13, 2012 at 20:42

Jon Clements's user avatar

Jon ClementsJon Clements

139k33 gold badges247 silver badges281 bronze badges

5

To avoid «dictionary changed size during iteration error».

For example: «when you try to delete some key»,

Just use ‘list’ with ‘.items()’. Here is a simple example:

my_dict = {
    'k1':1,
    'k2':2,
    'k3':3,
    'k4':4
 
    }
    
print(my_dict)

for key, val in list(my_dict.items()):
    if val == 2 or val == 4:
        my_dict.pop(key)

print(my_dict)

Output:

{'k1': 1, 'k2': 2, 'k3': 3, 'k4': 4}

{'k1': 1, 'k3': 3}

This is just an example. Change it based on your case/requirements.

Peter Mortensen's user avatar

answered Mar 19, 2021 at 22:59

K.A's user avatar

K.AK.A

1,40912 silver badges24 bronze badges

1

For Python 3:

{k:v for k,v in d.items() if v}

Ahmad's user avatar

Ahmad

2273 silver badges15 bronze badges

answered Jan 14, 2016 at 16:14

ucyo's user avatar

ucyoucyo

6357 silver badges16 bronze badges

2

You cannot iterate through a dictionary while it’s changing during a for loop. Make a casting to list and iterate over that list. It works for me.

    for key in list(d):
        if not d[key]:
            d.pop(key)

Peter Mortensen's user avatar

answered May 17, 2019 at 9:28

Alvaro Romero Diaz's user avatar

1

Python 3 does not allow deletion while iterating (using the for loop above) a dictionary. There are various alternatives to do it; one simple way is to change the line

for i in x.keys():

with

for i in list(x)

Peter Mortensen's user avatar

answered Dec 26, 2019 at 22:23

Hasham Beyg's user avatar

Hasham BeygHasham Beyg

3133 silver badges11 bronze badges

1

Use a list to collect the keys that should be removed; then use the pop dictionary method to remove the identified keys while iterating through the list (a separate object, so the error will not occur).

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
    if not d[i]:
        pop_list.append(i)

for x in pop_list:
    d.pop(x)

print(d)

Karl Knechtel's user avatar

Karl Knechtel

62.3k11 gold badges102 silver badges153 bronze badges

answered Oct 6, 2018 at 21:00

Rohit's user avatar

RohitRohit

273 bronze badges

1

For situations like this, I like to make a deep copy and loop through that copy while modifying the original dict.

If the lookup field is within a list, you can enumerate in the for loop of the list and then specify the position as the index to access the field in the original dict.

Peter Mortensen's user avatar

answered Oct 30, 2018 at 20:41

Ajayi Oluwaseun Emmanuel's user avatar

0

  • The Python «RuntimeError: dictionary changed size during iteration» occurs when we change the size of a dictionary when iterating over it.

  • To solve the error, use the copy() method to create a shallow copy of the dictionary that you can iterate over, e.g., my_dict.copy().

    my_dict = {'a': 1, 'b': 2, 'c': 3}
    
    for key in my_dict.copy():
        print(key)
        if key == 'b':
            del my_dict[key]
    
    print(my_dict) # 👉️ {'a': 1, 'c': 3}
    
  • You can also convert the keys of the dictionary to a list and iterate over the list of keys.

    my_dict = {'a': 1, 'b': 2, 'c': 3}
    
    for key in list(my_dict.keys()):
        print(key)
        if key == 'b':
            del my_dict[key]
    
    print(my_dict)  # 👉️ {'a': 1, 'c': 3}
    

Peter Mortensen's user avatar

answered Dec 6, 2022 at 22:11

Md Shayon's user avatar

1

you can make function and do this


    d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
    def remove_empty_list(d : dict ) -> dict:
        return {k:v  for k,v in d.items() if  v }
    print(remove_empty_list(d))

answered Sep 18 at 19:46

urek mazino's user avatar

Nested null values

Let’s say we have a dictionary with nested keys, some of which are null values:

dicti = {
"k0_l0":{
    "k0_l1": {
        "k0_l2": {
                "k0_0":None,
                "k1_1":1,
                "k2_2":2.2
                }
        },
        "k1_l1":None,
        "k2_l1":"not none",
        "k3_l1":[]
    },
    "k1_l0":"l0"
}

Then we can remove the null values using this function:

def pop_nested_nulls(dicti):
    for k in list(dicti):
        if isinstance(dicti[k], dict):
            dicti[k] = pop_nested_nulls(dicti[k])
        elif not dicti[k]:
            dicti.pop(k)
    return dicti

Output for pop_nested_nulls(dicti)

{'k0_l0': {'k0_l1': {'k0_l2': {'k1_1': 1,
                               'k2_2': 2.2}},
           'k2_l1': 'not '
                    'none'},
 'k1_l0': 'l0'}

answered Jan 19 at 13:54

Echo9k's user avatar

Echo9kEcho9k

5546 silver badges9 bronze badges

1

This approach can be used if the values in the dictionary are also unique:

keyToBeDeleted = None
for k, v in mydict.items():
    if(v == match):
        keyToBeDeleted = k
        break
mydict.pop(keyToBeDeleted, None)

Karl Knechtel's user avatar

Karl Knechtel

62.3k11 gold badges102 silver badges153 bronze badges

answered Nov 23, 2022 at 13:39

Ganesh S's user avatar

Ganesh SGanesh S

3816 silver badges26 bronze badges

1

Suppose I have a dictionary of lists:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

Now I want to remove key-value pairs where the values are empty lists. I tried this code:

for i in d:
    if not d[i]:
        d.pop(i)

but this gives an error:

RuntimeError: dictionary changed size during iteration

I understand that entries can’t be added or removed from a dictionary while iterating through it. How can I work around this limitation in order to solve the problem?


See Modifying a Python dict while iterating over it for citations that this can cause problems, and why.

Karl Knechtel's user avatar

Karl Knechtel

62.3k11 gold badges102 silver badges153 bronze badges

asked Aug 13, 2012 at 20:30

user1530318's user avatar

user1530318user1530318

25.6k15 gold badges37 silver badges48 bronze badges

3

In Python 3.x and 2.x you can use use list to force a copy of the keys to be made:

for i in list(d):

In Python 2.x calling .keys made a copy of the keys that you could iterate over while modifying the dict:

for i in d.keys():

but on Python 3.x, .keys returns a view object instead, so it won’t fix your error.

Boris Verkhovskiy's user avatar

answered Aug 13, 2012 at 20:33

Mark Byers's user avatar

Mark ByersMark Byers

813k193 gold badges1583 silver badges1452 bronze badges

7

You only need to use copy:

This way you iterate over the original dictionary fields and on the fly can change the desired dict d.
It works on each Python version, so it’s more clear.

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

(BTW — Generally to iterate over copy of your data structure, instead of using .copy for dictionaries or slicing [:] for lists, you can use import copy -> copy.copy (for shallow copy which is equivalent to copy that is supported by dictionaries or slicing [:] that is supported by lists) or copy.deepcopy on your data structure.)

mkrieger1's user avatar

mkrieger1

19.3k5 gold badges54 silver badges65 bronze badges

answered Mar 31, 2016 at 10:35

Alon Elharar's user avatar

1

Just use dictionary comprehension to copy the relevant items into a new dict:

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

For this in Python 2:

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

Peter Mortensen's user avatar

answered Aug 13, 2012 at 20:38

Maria Zverina's user avatar

Maria ZverinaMaria Zverina

10.9k3 gold badges44 silver badges61 bronze badges

3

This worked for me:

d = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(d.items()):
    if value == '':
        del d[key]
print(d)
# {1: 'a', 3: 'b', 6: 'c'}

Casting the dictionary items to list creates a list of its items, so you can iterate over it and avoid the RuntimeError.

wjandrea's user avatar

wjandrea

28.5k9 gold badges62 silver badges82 bronze badges

answered Jan 31, 2020 at 9:05

singrium's user avatar

singriumsingrium

2,7465 gold badges32 silver badges45 bronze badges

0

I would try to avoid inserting empty lists in the first place, but, would generally use:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

If prior to 2.7:

d = dict( (k, v) for k,v in d.iteritems() if v )

or just:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

answered Aug 13, 2012 at 20:42

Jon Clements's user avatar

Jon ClementsJon Clements

139k33 gold badges247 silver badges281 bronze badges

5

To avoid «dictionary changed size during iteration error».

For example: «when you try to delete some key»,

Just use ‘list’ with ‘.items()’. Here is a simple example:

my_dict = {
    'k1':1,
    'k2':2,
    'k3':3,
    'k4':4
 
    }
    
print(my_dict)

for key, val in list(my_dict.items()):
    if val == 2 or val == 4:
        my_dict.pop(key)

print(my_dict)

Output:

{'k1': 1, 'k2': 2, 'k3': 3, 'k4': 4}

{'k1': 1, 'k3': 3}

This is just an example. Change it based on your case/requirements.

Peter Mortensen's user avatar

answered Mar 19, 2021 at 22:59

K.A's user avatar

K.AK.A

1,40912 silver badges24 bronze badges

1

For Python 3:

{k:v for k,v in d.items() if v}

Ahmad's user avatar

Ahmad

2273 silver badges15 bronze badges

answered Jan 14, 2016 at 16:14

ucyo's user avatar

ucyoucyo

6357 silver badges16 bronze badges

2

You cannot iterate through a dictionary while it’s changing during a for loop. Make a casting to list and iterate over that list. It works for me.

    for key in list(d):
        if not d[key]:
            d.pop(key)

Peter Mortensen's user avatar

answered May 17, 2019 at 9:28

Alvaro Romero Diaz's user avatar

1

Python 3 does not allow deletion while iterating (using the for loop above) a dictionary. There are various alternatives to do it; one simple way is to change the line

for i in x.keys():

with

for i in list(x)

Peter Mortensen's user avatar

answered Dec 26, 2019 at 22:23

Hasham Beyg's user avatar

Hasham BeygHasham Beyg

3133 silver badges11 bronze badges

1

Use a list to collect the keys that should be removed; then use the pop dictionary method to remove the identified keys while iterating through the list (a separate object, so the error will not occur).

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
    if not d[i]:
        pop_list.append(i)

for x in pop_list:
    d.pop(x)

print(d)

Karl Knechtel's user avatar

Karl Knechtel

62.3k11 gold badges102 silver badges153 bronze badges

answered Oct 6, 2018 at 21:00

Rohit's user avatar

RohitRohit

273 bronze badges

1

For situations like this, I like to make a deep copy and loop through that copy while modifying the original dict.

If the lookup field is within a list, you can enumerate in the for loop of the list and then specify the position as the index to access the field in the original dict.

Peter Mortensen's user avatar

answered Oct 30, 2018 at 20:41

Ajayi Oluwaseun Emmanuel's user avatar

0

  • The Python «RuntimeError: dictionary changed size during iteration» occurs when we change the size of a dictionary when iterating over it.

  • To solve the error, use the copy() method to create a shallow copy of the dictionary that you can iterate over, e.g., my_dict.copy().

    my_dict = {'a': 1, 'b': 2, 'c': 3}
    
    for key in my_dict.copy():
        print(key)
        if key == 'b':
            del my_dict[key]
    
    print(my_dict) # 👉️ {'a': 1, 'c': 3}
    
  • You can also convert the keys of the dictionary to a list and iterate over the list of keys.

    my_dict = {'a': 1, 'b': 2, 'c': 3}
    
    for key in list(my_dict.keys()):
        print(key)
        if key == 'b':
            del my_dict[key]
    
    print(my_dict)  # 👉️ {'a': 1, 'c': 3}
    

Peter Mortensen's user avatar

answered Dec 6, 2022 at 22:11

Md Shayon's user avatar

1

you can make function and do this


    d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
    def remove_empty_list(d : dict ) -> dict:
        return {k:v  for k,v in d.items() if  v }
    print(remove_empty_list(d))

answered Sep 18 at 19:46

urek mazino's user avatar

Nested null values

Let’s say we have a dictionary with nested keys, some of which are null values:

dicti = {
"k0_l0":{
    "k0_l1": {
        "k0_l2": {
                "k0_0":None,
                "k1_1":1,
                "k2_2":2.2
                }
        },
        "k1_l1":None,
        "k2_l1":"not none",
        "k3_l1":[]
    },
    "k1_l0":"l0"
}

Then we can remove the null values using this function:

def pop_nested_nulls(dicti):
    for k in list(dicti):
        if isinstance(dicti[k], dict):
            dicti[k] = pop_nested_nulls(dicti[k])
        elif not dicti[k]:
            dicti.pop(k)
    return dicti

Output for pop_nested_nulls(dicti)

{'k0_l0': {'k0_l1': {'k0_l2': {'k1_1': 1,
                               'k2_2': 2.2}},
           'k2_l1': 'not '
                    'none'},
 'k1_l0': 'l0'}

answered Jan 19 at 13:54

Echo9k's user avatar

Echo9kEcho9k

5546 silver badges9 bronze badges

1

This approach can be used if the values in the dictionary are also unique:

keyToBeDeleted = None
for k, v in mydict.items():
    if(v == match):
        keyToBeDeleted = k
        break
mydict.pop(keyToBeDeleted, None)

Karl Knechtel's user avatar

Karl Knechtel

62.3k11 gold badges102 silver badges153 bronze badges

answered Nov 23, 2022 at 13:39

Ganesh S's user avatar

Ganesh SGanesh S

3816 silver badges26 bronze badges

1

Проблема связана с тем, что вы пытаетесь модифицировать словарь, по элементам которого итерируетесь.
В условиях вашего кода это не проблема. Но вы должны сначала прежде чем начнётся цикл с удалениями) взять список пар элементов из вашего словаря.
products.items() возвращает не список, а итератор по элементам словаря. Само собой этот итератор ломается как только вы удаляете из словаря первый элемент.
Но если этот итератор до первого удаления полностью превратить в список, то удалять потом можно что хотите.

for product_id, value in list(products.items()):
    if value['g_1'] == 0:
        del products[product_id]

Или так:

new_products_dict = {k: v for k, v in products.items() if v['g_1'] != 0}

Здесь создаётся новый словарь из элементов, которые удовлетворяют вашим условиям.

products = {
    '00000234': {'value1': 0, 'value2': 23},
    '567333': {'value1': 5, 'value2': 23},
    '23234243': {'value1': 25, 'value2': 23},
}
products = {product_id: values for product_id, values in products.items() if values['value1']}
print(products)
# {'567333': {'value1': 5, 'value2': 23}, '23234243': {'value1': 25, 'value2': 23}}

Или

spoiler

Создай еще один словарь, проверяй условие в цикле
если проходит по твоим параметрам (что нет параметров равных 0) добавляй в новый словарь,
по окончании цикла возвращай новый словарь

I want pop out all the large values and its keys in a dictionary, and keep the smallest. Here is the part of my program

for key,value in dictionary.items():
    for key1, value1 in dictionary.items(): 
            if key1!= key and value > value1:
                dictionary.pop(key)             
    print (dictionary)  

Which results in

RuntimeError: dictionary changed size during iteration    

How can I avoid this error?

martineau's user avatar

martineau

120k25 gold badges170 silver badges303 bronze badges

asked Nov 22, 2012 at 20:34

YXH's user avatar

8

In Python3, Try

for key in list(dict.keys()):
    if condition:
        matched
        del dict[key]

1 more thing should be careful when looping a dict to update its key:

Code1:

keyPrefix = ‘keyA’
for key, value in Dict.items():
    newkey = ‘/’.join([keyPrefix, key])
    Dict[newkey] = Dict.pop(key)

Code2:

keyPrefix = ‘keyA’
for key, value in Dict.keys():
    newkey = ‘/’.join([keyPrefix, key])
    Dict[newkey] = Dict.pop(key)

Result of code1/code2 is:

{‘keyA/keyA/keyB’ : ”, ‘keyA/keyA/keyA’: ”}

My way to resolve this unexpected result:

    Dict = {‘/’.join([keyPrefix, key]): value for key, value in Dict.items()}

Link: https://blog.gainskills.top/2016/07/21/loop-a-dict-to-update-key/

answered Sep 23, 2015 at 6:38

Xb74Dkjb's user avatar

Xb74DkjbXb74Dkjb

9829 silver badges20 bronze badges

0

Alternative solutions

If you’re looking for the smallest value in the dictionary you can do this:

min(dictionary.values())

If you cannot use min, you can use sorted:

sorted(dictionary.values())[0]

Why do I get this error?

On a side note, the reason you’re experiencing an Runtime Error is that in the inner loop you modify the iterator your outer loop is based upon. When you pop an entry that is yet to be reached by the outer loop and the outer iterator reaches it, it tries to access a removed element, thus causing the error.
If you try to execute your code on Python 2.7 (instead of 3.x) you’ll get, in fact, a Key Error.

What can I do to avoid the error?

If you want to modify an iterable inside a loop based on its iterator you should use a deep copy of it.

answered Nov 22, 2012 at 20:55

Nadir Sampaoli's user avatar

Nadir SampaoliNadir Sampaoli

5,4474 gold badges22 silver badges32 bronze badges

6

You can use copy.deepcopy to make a copy of the original dict, loop over the copy while change the original one.

from copy import deepcopy

d=dict()
for i in range(5):
    d[i]=str(i)

k=deepcopy(d)

d[2]="22"
print(k[2])
#The result will be 2.

Your problem is iterate over something that you are changing.

answered Nov 22, 2012 at 21:02

Arthur Julião's user avatar

Arthur JuliãoArthur Julião

8491 gold badge14 silver badges29 bronze badges

1

Record the key during the loop and then do dictionary.pop(key) when loop is done. Like this:

for key,value in dictionary.items():
    for key1, value1 in dictionary.items(): 
            if key1!= key and value > value1:
                storedvalue = key
    dictionary.pop(key)  

David Heffernan's user avatar

answered Nov 22, 2012 at 20:37

Odif Yltsaeb's user avatar

Odif YltsaebOdif Yltsaeb

5,57512 gold badges49 silver badges80 bronze badges

2

Here is one way to solve it:

  1. From the dictionary, get a list of keys, sorted by value
  2. Since the first key in this list has the smallest value, you can do what you want with it.

Here is a sample:

# A list of grades, not in any order
grades = dict(John=95,Amanda=89,Jake=91,Betty=97)

# students is a list of students, sorted from lowest to highest grade
students = sorted(grades, key=lambda k: grades[k])

print 'Grades from lowest to highest:'
for student in students:
    print '{0} {1}'.format(grades[student], student)

lowest_student = students[0]
highest_student = students[-1]
print 'Lowest grade of {0} belongs to {1}'.format(grades[lowest_student], lowest_student)
print 'Highest grade of {0} belongs to {1}'.format(grades[highest_student], highest_student)

The secret sauce here is in the sorted() function: instead of sorting by keys, we sorted by values.

answered Nov 22, 2012 at 20:57

Hai Vu's user avatar

Hai VuHai Vu

38k11 gold badges68 silver badges93 bronze badges

If you want to just keep the key with the smallest value, I would do it by first finding that item and then creating a new dictionary containing only it. If your dictionary was d, something like this would do that in one line:

d = dict((min(d.items(), key=lambda item: item[1]),))

This will not only avoid any issues about updating the dictionary while iterating it, it is probably faster than removing all the other elements.

If you must do the modifications in-place for some reason, the following would work because it makes a copy of all the keys before modifying the dictionary:

key_to_keep = min(d.items(), key=lambda item: item[1])[0]

for key in list(d):
    if key != key_to_keep:
        d.pop(key)

answered Nov 22, 2012 at 21:09

martineau's user avatar

martineaumartineau

120k25 gold badges170 silver badges303 bronze badges

As I read your loop right now, you’re looking to keep only the single smallest element, but without using min. So do the opposite of what your code does now, check if value1 < minValueSoFar, if so, keep key1 as minKeySoFar. Then at the end of the loop (as Zayatzz suggested), do a dictionary.pop(minKeySoFar)

As an aside, I note that the key1!=key test is irrelevant and computationally inefficient assuming a reasonably long list.

minValueSoFar = 9999999;   # or whatever
for key,value in dictionary.items():
    if value < minValueSoFar:
        minValueSoFar = value
        minKeySoFar = key
dictionary.pop(minKeySoFar)   # or whatever else you want to do with the result

answered Nov 22, 2012 at 20:48

mackworth's user avatar

mackworthmackworth

5,8732 gold badges29 silver badges49 bronze badges

An alternative solution to dictionary changed size during iteration:

for key,value in list(dictionary.items()):
    for key1, value1 in list(dictionary.items()): 
            if key1!= key and value > value1:
                dictionary.pop(key)             
print (dictionary)  

Better use it with caution! when using this type of code, because list(dictionary.items()) calculated when the complier enters first time to loop. Therefore any change made on dictionary won’t affect process inside the current loop.

answered Dec 5, 2019 at 6:44

furkanayd's user avatar

furkanaydfurkanayd

8117 silver badges19 bronze badges

You could create a list with the vaules you want to delete and than run a second for loop:

for entry in (listofkeystopop):
        dictionary.pop(entry)

answered Jan 13, 2021 at 22:09

r0tt's user avatar

r0ttr0tt

3793 silver badges20 bronze badges

  1. Creating a Shallow Copy of the Dictionary
  2. Casting Dictionary Items to a List
  3. Appending Keys to an Empty List

Fix Error - Dictionary Changed Size During Iteration

This Runtime error occurs when we remove, modify, or add new entries in a dictionary object during iteration. This error occurs when iterating through a dictionary but virtually all iterable objects regardless of the programming language used.

The code snippet below illustrates how this error comes about when iterating through a dictionary and making changes simultaneously.

cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021
 }

for x in cars.keys():
  cars["color"] = "white"
print(x)

In the code block above, we add a new item to the original dictionary while iterating. This will return a Runtime Error, letting us know that the dictionary size changed during iteration, implying that we cannot modify the dictionary while iterating simultaneously.

Sample Code:

Traceback (most recent call last):
File "<string>", line 8, in <module>
RuntimeError: dictionary changed size during iteration

While performing any iteration to an object, both deletion, addition, or modification are considered an alteration and cannot be performed while iterating. The code example below demonstrates that this error will also persist if we modify the dictionary while iterating. Therefore, we will still get the same error if we remove an existing item from a dictionary while iterating.

Sample Code:

cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021
 }

for x in cars.keys():
    del cars["model"]
print(cars)

Output:

Traceback (most recent call last):
File "<string>", line 8, in <module>
RuntimeError: dictionary changed size during iteration

In Python 3, iterating over a changing object is considered a bad style of writing code and unsafe. Generally, in Programming, we cannot mutate an object while iterating over it simultaneously; this rule extends to iterables such as lists and even arrays.

However, if a function mutates an object, we must make sure that the function only mutates a copy of the original object, leaving the original object intact. This is one of the widely used approaches of making alterations to objects while iterating through them simultaneously.

This is a good practice and the best way of avoiding instances of creating an infinite loop that may eventually lead to memory exhaustion. Several solutions can be used to handle this error, and we will discuss each one here.

Creating a Shallow Copy of the Dictionary

Python provides us with the copy() module that allows us to create a copy of an object with no binding to the original object. This lets us freely modify the copy of the object, leaving the original object intact.

Note that the same cannot be realized by using the assignment operator in Python. Using the assignment operator does not create a copy of the original object but rather a variable that refers to the original object.

Therefore any modifications made to the new object will also affect the original object. New developers often misuse this operator.

Sample Code:

import copy
cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021,
 }

#creating a shallow copy
cars_copy = copy.copy(cars)

for x in cars_copy.keys():
    cars["color"] = "black"
    
print(cars)
print(cars_copy)

Output:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021}

In the sample code provided, we have used the copy module’s copy function to create a dictionary copy that we can freely iterate without affecting the original dictionary. Making changes to a dictionary copy allows us to iterate over the dictionary without encountering an error.

Alternatively, we can use the ** operator that is often referred to as the two asterisk operators to rewrite the code above, as shown below.

Sample Code:

cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021
 }

#creating a shallow copy
cars_copy = {**cars}


for x in cars_copy.keys():
  cars["color"] = "black"
  
print(cars)

The ** operator can take key-value pairs from one dictionary and dump them into another dictionary.

Although the operator is widely used to pass in keyword arguments in Python, we have used the operator to unpack the dictionary and obtain the key-value pairs in the code above. We then create a copy of the dictionary and dump the unpacked values in this new dictionary.

Output:

'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}

Deleting a key-value pair from a dictionary is no exception either when performing an iteration and thus should follow a similar approach. Therefore using the same procedure, we will delete the key named model and its value Model S Plaid as shown below.

Sample Code:

import copy
cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021,
 "color": "black"
 }
 
cars_copy = copy.copy(cars)
 
for x in cars_copy.keys():
    if x == "model":
        del cars["model"]
   
print(cars)

Output:

{'brand': 'Tesla', 'year': 2021, 'color': 'black'}

Another solution would be to create a copy of the keys that we can then iterate over while modifying the dictionary. However, this can only work in Python 2 and not Python 3 because when done in Python 3, the keys do not return the iterable.

Sample Code:

cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021,
 "color": "black"
 }

key_copys = list(cars.keys())
print(key_copys)

for key in list(key_copys):
    if cars[key] == "model":
        cars.pop("model")
        
print(cars)

Sample Output:

['brand', 'model', 'year', 'color']
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}

Casting Dictionary Items to a List

Since we cannot iterate over the dictionary while making changes, we can instead create a casting list and iterate over the list while making changes to the dictionary. Iterating over the casting list instead of the original dictionary does not return a Runtime error.

Sample Code:

cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021
 }

for i in list(cars):
    cars["color"] = "black"
    
print(cars)

Output:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}

Appending Keys to an Empty List

To avoid changing the dictionary while iterating, we can create an empty list containing the dictionary keys while we perform the iteration. Using this empty list, we can append all the keys that we want to remove or change and then use the pop() function to remove the keys or the append function to add new key-value pairs.

This can be executed as shown in the code below.

Sample Code:

cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021
 }

list = []

for i in cars:
    list.append(i)
    
for x in list:
    if x == "model":
        cars.pop(x)
    
print(cars)

Output:

{'brand': 'Tesla', 'year': 2021}

As shown below, we can add a new key-value pair to the dictionary using the same procedure while iterating using the for loop.

Sample Code:

cars = {
 "brand": "Tesla",
 "model": "Model S Plaid",
 "year":  2021
 }
 
list = []

for i in cars:
    list.append(i)
    
for x in list:
    cars["color"] = "black"
  
print(cars)

Output:

{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}

Понравилась статья? Поделить с друзьями:
  • Ошибка direct3d sims 2
  • Ошибка dhof старлайн
  • Ошибка dhf холодильника lg
  • Ошибка dhcp на принтере xerox b215
  • Ошибка df394 рено кангу