RuntimeError: OrderedDict mutated during iteration (Python3) - python

Getting the error mentioned in the title. The below mentioned functioned is called by another function that is called through a POST api.
Error is on the line below the print statement. Dont know what the error means and why its coming. The same code used to run a week back.
def remove_individual_stops(ordered_parkstop_dict, relevant_data):
new_ordered_parkstop_dict = ordered_parkstop_dict
for key, value in ordered_parkstop_dict.items():
if len(value) == 0:
for k,v in ordered_parkstop_dict.items():
if key in v:
new_ordered_parkstop_dict.pop(key)
print (type(ordered_parkstop_dict), ordered_parkstop_dict)
for k,v in ordered_parkstop_dict.items():
klist = []
keylist = []
if value and v:
if len(v)==1 and len(value)==1:
klist.append(k), keylist.append(key)
if (keylist == v) and (klist == value and len(value) == 1):
new_ordered_parkstop_dict.pop(key)
return new_ordered_parkstop_dict

You assigned new_ordered_parkstop_dict with a reference of the ordered_parkstop_dict dict, so when you iterate over ordered_parkstop_dict.items() and mutate new_ordered_parkstop_dict by popping it, you mutate ordered_parkstop_dict too, which can't be done since your loop is iterating over ordered_parkstop_dict.
You should assign a copy of ordered_parkstop_dict to new_ordered_parkstop_dict instead. Change:
new_ordered_parkstop_dict = ordered_parkstop_dict
to:
new_ordered_parkstop_dict = ordered_parkstop_dict.copy()

Related

Append to a list in Python [duplicate]

I have some code that prints data from a global dictionary named cal:
def show_todo():
for key, value in cal.items():
print(value[0], key)
However, I want to use this code as part of a Discord bot. In order for the bot to work properly, I need to return the data to another function that will actually send the message to the Discord chat. Using print like above means that the message is displayed in my local console window, and the chat just sees None.
I tried to fix it by using return instead:
def show_todo():
for key, value in cal.items():
return(value[0], key)
but this way, the for loop does not work properly. I only get at most one key-value pair from the dictionary.
How can I fix this so that all of the data is returned?
Using a return inside of a loop will break it and exit the function even if the iteration is still not finished.
For example:
def num():
# Here there will be only one iteration
# For number == 1 => 1 % 2 = 1
# So, break the loop and return the number
for number in range(1, 10):
if number % 2:
return number
>>> num()
1
In some cases we need to break the loop if some conditions are met. However, in your current code, breaking the loop before finishing it is unintentional.
Instead of that, you can use a different approach:
Yielding your data
def show_todo():
# Create a generator
for key, value in cal.items():
yield value[0], key
You can call it like:
a = list(show_todo()) # or tuple(show_todo())
or you can iterate through it:
for v, k in show_todo(): ...
Putting your data into a list or other container
Append your data to a list, then return it after the end of your loop:
def show_todo():
my_list = []
for key, value in cal.items():
my_list.append((value[0], key))
return my_list
Or use a list comprehension:
def show_todo():
return [(value[0], key) for key, value in cal.items()]
Use a generator syntax (excellent explanation on SO here):
def show_todo():
for key, value in cal.items():
yield value[0], key
for value, key in show_todo():
print(value, key)

How to get each and every value of my for loop [duplicate]

I have some code that prints data from a global dictionary named cal:
def show_todo():
for key, value in cal.items():
print(value[0], key)
However, I want to use this code as part of a Discord bot. In order for the bot to work properly, I need to return the data to another function that will actually send the message to the Discord chat. Using print like above means that the message is displayed in my local console window, and the chat just sees None.
I tried to fix it by using return instead:
def show_todo():
for key, value in cal.items():
return(value[0], key)
but this way, the for loop does not work properly. I only get at most one key-value pair from the dictionary.
How can I fix this so that all of the data is returned?
Using a return inside of a loop will break it and exit the function even if the iteration is still not finished.
For example:
def num():
# Here there will be only one iteration
# For number == 1 => 1 % 2 = 1
# So, break the loop and return the number
for number in range(1, 10):
if number % 2:
return number
>>> num()
1
In some cases we need to break the loop if some conditions are met. However, in your current code, breaking the loop before finishing it is unintentional.
Instead of that, you can use a different approach:
Yielding your data
def show_todo():
# Create a generator
for key, value in cal.items():
yield value[0], key
You can call it like:
a = list(show_todo()) # or tuple(show_todo())
or you can iterate through it:
for v, k in show_todo(): ...
Putting your data into a list or other container
Append your data to a list, then return it after the end of your loop:
def show_todo():
my_list = []
for key, value in cal.items():
my_list.append((value[0], key))
return my_list
Or use a list comprehension:
def show_todo():
return [(value[0], key) for key, value in cal.items()]
Use a generator syntax (excellent explanation on SO here):
def show_todo():
for key, value in cal.items():
yield value[0], key
for value, key in show_todo():
print(value, key)

Return next key of a given dictionary key, python 3.6+

I am trying to find a way to get the next key of a Python 3.6+ (which are ordered)
For example:
dict = {'one':'value 1','two':'value 2','three':'value 3'}
What I am trying to achieve is a function to return the next key. something like:
next_key(dict, current_key='two') # -> should return 'three'
This is what I have so far:
def next_key(dict,key):
key_iter = iter(dict) # create iterator with keys
while k := next(key_iter): #(not sure if this is a valid way to iterate over an iterator)
if k == key:
#key found! return next key
try: #added this to handle when key is the last key of the list
return(next(key_iter))
except:
return False
return False
well, that is the basic idea, I think I am close, but this code gives a StopIteration error. Please help.
Thank you!
An iterator way...
def next_key(dict, key):
keys = iter(dict)
key in keys
return next(keys, False)
Demo:
>>> next_key(dict, 'two')
'three'
>>> next_key(dict, 'three')
False
>>> next_key(dict, 'four')
False
Looping while k := next(key_iter) doesn’t stop correctly. Iterating manually with iter is done either by catching StopIteration:
iterator = iter(some_iterable)
while True:
try:
value = next(iterator)
except StopIteration:
# no more items
or by passing a default value to next and letting it catch StopIteration for you, then checking for that default value (but you need to pick a default value that won’t appear in your iterable!):
iterator = iter(some_iterable)
while (value := next(iterator, None)) is not None:
# …
# no more items
but iterators are, themselves, iterable, so you can skip all that and use a plain ol’ for loop:
iterator = iter(some_iterable)
for value in iterator:
# …
# no more items
which translates into your example as:
def next_key(d, key):
key_iter = iter(d)
for k in key_iter:
if k == key:
return next(key_iter, None)
return None
You can get the keys of the dictionary as list and use index() to get the next key. You can also check for IndexError with try/except block:
my_dict = {'one':'value 1','two':'value 2','three':'value 3'}
def next_key(d, key):
dict_keys = list(d.keys())
try:
return dict_keys[dict_keys.index(key) + 1]
except IndexError:
print('Item index does not exist')
return -1
nk = next_key(my_dict, key="two")
print(nk)
And you better not use dict, list etc as variable names.
# Python3 code to demonstrate working of
# Getting next key in dictionary Using list() + index()
# initializing dictionary
test_dict = {'one':'value 1','two':'value 2','three':'value 3'}
def get_next_key(dic, current_key):
""" get the next key of a dictionary.
Parameters
----------
dic: dict
current_key: string
Return
------
next_key: string, represent the next key in dictionary.
or
False If the value passed in current_key can not be found in the dictionary keys,
or it is last key in the dictionary
"""
l=list(dic) # convert the dict keys to a list
try:
next_key=l[l.index(current_key) + 1] # using index method to get next key
except (ValueError, IndexError):
return False
return next_key
get_next_key(test_dict, 'two')
'three'
get_next_key(test_dict, 'three')
False
get_next_key(test_dict, 'one')
'two'
get_next_key(test_dict, 'NOT EXISTS')
False

function that looks up keys in a dictionary until there is no more associated values

I need help creating a function that goes through a given dictionary. The value associated with that key may be another key to the dictionary. i need the function to keep looking up the keys until it reaches a key that has no associated value.
def follow_me(d, s):
while d:
if s in d:
return d[s]
I can return the value in the dictionary that s equals to but I've no idea how to iterate through it until I get a value that has no associated value. So I can get the value that badger is doe, but how do I iterate through the dictionary until I get fox and then fox to hen etc.
d = {'badger':'doe', 'doe':'fox', 'fox':'hen','hen':'flea',
'sparrow':'spider', 'zebra':'lion', 'lion':'zebra'}
print(follow_me(d, 'badger'))
print(follow_me(d, 'fox'))
print(follow_me(d, 'sparrow'))
print(follow_me(d, 'zebra'))
print(follow_me(d, 'aardvark'))
and this is what I currently have of the function that makes sense to me because everything else I've tried is just wrong.
def follow_me(d, s):
while d:
if s in d:
return d[s]
and the output needs to be:
flea
flea
spider
aardvark
but my code right now is producing:
doe
hen
spider
lion
To extend on the other answers, which are still valid. If you have a very large dictionary then using key not in dic.keys() or k in d iterates through all keys every loop.
To go around this, one can use a try catch:
def follow_me(dic, key):
while True:
if key not in dic.keys():
return key
key = dic[key]
def follow_me2(dic, key):
try:
while True:
key = dic[key]
except Exception as e:
return key
import time
d = { i: (i+1) for i in range(10000000) }
start = time.time()
follow_me(d, 0)
print("Using 'in' takes", time.time() - start,"s")
start = time.time()
follow_me2(d, 0)
print("Using 'try' takes", time.time() - start,"s")
gives the output:
Using 'in' takes 2.476428747177124 s
Using 'try' takes 0.9100546836853027 s
I think this is what you are looking for, though your problem description is very unclear:
def follow_me(d, k):
while k in d:
k = d[k]
return k
Note that the loop in this function will run forever if there is a cycle between keys and values in your dictionary. Your example has one between 'lion' and 'zebra', and it's not entirely clear how you intend such a cycle to be broken. If you want to expand each key only once, you could handle it by keeping track of the values you've seen so far in a set:
def follow_me(d, k):
seen = set()
while k in d and k not in seen:
seen.add(k)
k = d[k]
return k
This will return whichever key in the cycle you reach first (so follow_me(d, 'zebra') with your example dictionary will return 'zebra' after going zebra => lion => zebra). If you want some other outcome, you'd need different logic and it might be tricky to do.
If you request a key that's not in the dictionary (like 'aardvark' in your example), the requested key will be returned immediately. You could add special handling for the first key you look up, but it would again make things more complicated.
Considering the existence of infinite loops this has to be handled. Your description isn't clear about what should happen in this case.
def follow_me(d, key):
visited_keys = []
while key not in visited_keys and d[key]:
visited_keys.append(key)
key = d[key]
if not d[key]:
return key
return "this hunt has no end"

Break in for dictionary loop

I have a function that loops through a JSON object. The object often comes back in several different formats and I need to find a specific key and print the value of it. Also, sometimes the key I need is listed multiple times. So, I only want to print the first one. I've tried using a break but it doesn't seem to work. Any ideas why?
def deep(o):
for k, v in o.iteritems():
if k.lower() == 'key_i_look_for':
print v
break
elif type(v) is dict:
deep(o[k])
elif type(v) is list:
for i in v:
if type(i) is dict:
deep(i)
Although break works just fine, you are also recursing through a nested structure and never returning anything. You are ignoring the recursive searches in the outer calls; if one of them finds the key, the next recursive call is still executed.
You need to return recursive calls too, and take into account that some searches may not yield anything (return None):
def deep(o):
for k, v in o.iteritems():
if k.lower() == 'key_i_look_for':
return v
if not isinstance(v, list):
v = [v]
for i in v:
if isinstance(i, dict):
result = deep(i)
if result is not None:
return result
I've cleaned up the logic a little to handle list vs. dictionary values a little easier, and switched to using isinstance() to allow for subclasses too.
This version returns the first key it finds; any recursive calls are checked for None to see if that recursive call turned up something, and break of the search there.

Categories