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)
Related
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)
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"
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()
I have the following two codes, similar except one function prints, while another returns the keys in a dictionary.
My question is, why does the one that returns, only show the first line when the print is called on the function?
**VERSION 1**
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
for i in dict.keys():
return (i)
X = catsfunc(ColourOfCats)
print (X)
**VERSION 2**
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
for i in dict.keys():
print (i)
catsfunc(ColourOfCats)
Thank you for your assistance.
Because a function can only return once.
The return statement terminates the execution of a function and
returns control to the calling function. Execution resumes in the
calling function at the point immediately following the call.
So in your VERSION 1 at line 8 when it return the i then return statement terminates the execution of a function and it so for loop doesn't go for second iteration for next value of dict.keys().
If you want to return all the result then never return iterator , instead store the output in a list , dict , set etc and at last return that
Here is an example for you:
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
result=[]
for i in dict.keys():
result.append(i) #instead of return here , store the output to a list
return result #now return
X = catsfunc(ColourOfCats)
print (X)
output:
['timmy', 'john', 'sam']
When you use return statement in function function will return the value and stops the execution and return control flow to calling line; hence even if you use for loop; when function returns first value another loops aren't executing whereas when you use print it is just printing the statement which is not causing to break the function flow.
However you can make generator function using yield keyword as below:
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
for i in dict.keys():
yield i
X = catsfunc(ColourOfCats) # X is now generator object
for items in X: # iterate generator object
print (items)
Is there a simple way to detect the last iteration while iterating over a dictionary using iteritems()?
There is an ugly way to do this:
for i, (k, v) in enumerate(your_dict.items()):
if i == len(your_dict)-1:
# do special stuff here
But you should really consider if you need this. I am almost certain that there is another way.
as others have stated, dictionaries have no defined order, so it's hard to imagine why you would need this, but here it is
last = None
for current in your_dict.iteritems():
if last is not None:
# process last
last = current
# now last contains the last thing in dict.iteritems()
if last is not None: # this could happen if the dict was empty
# process the last item
it = spam_dict.iteritems()
try:
eggs1 = it.next()
while True:
eggs2 = it.next()
do_something(eggs1)
eggs1 = eggs2
except StopIteration:
do_final(eggs1)
Quick and quite dirty. Does it solve your issue?
I know this late, but here's how I've solved this issue:
dictItemCount = len(dict)
dictPosition = 1
for key,value in dict
if(dictPosition = dictItemCount):
print 'last item in dictionary'
dictPosition += 1
This is a special case of this broader question. My suggestion was to create an enumerate-like generator that returns -1 on the last item:
def annotate(gen):
prev_i, prev_val = 0, gen.next()
for i, val in enumerate(gen, start=1):
yield prev_i, prev_val
prev_i, prev_val = i, val
yield '-1', prev_val
Add gen = iter(gen) if you want it to handle sequences as well as generators.
I recently had this issue, I thought this was the most elegant solution because it allowed you to write for i,value,isLast in lastEnumerate(...)::
def lastEnumerate(iterator):
x = list(iterator)
for i,value in enumerate(x):
yield i,value,i==len(x)-1
For example:
for i,value,isLast in lastEnumerate(range(5)):
print(value)
if not isLast:
print(',')
The last item in a for loop hangs around after the for loop anyway:
for current_item in my_dict:
do_something(current_item)
try:
do_last(current_item)
except NameError:
print "my_dict was empty"
Even if the name "current_item" is in use before the for loop, attempting to loop over an empty dict seems to have the effect of deleting current_item, hence the NameError
You stated in an above comment that you need this to construct the WHERE clause of an SQL SELECT statement. Perhaps this will help:
def make_filter(colname, value):
if isinstance(value, str):
if '%' in value:
return "%s LIKE '%s'" % (colname, value)
else:
return "%s = '%s'" % (colname, value)
return "%s = %s" % (colname, value)
filters = {'USER_ID':'123456', 'CHECK_NUM':23459, 'CHECK_STATUS':'C%'}
whereclause = 'WHERE '+'\nAND '.join(make_filter(*x) for x in filters.iteritems())
print whereclause
which prints
WHERE CHECK_NUM = 23459
AND CHECK_STATUS LIKE 'C%'
AND USER_ID = '123456'
The approach that makes the most sense is to wrap the loop in some call which contains a hook to call your post-iteration functionality afterwards.
This could be implemented as context manager and called through a 'with' statement or, for older versions of Python, you could use the old 'try:' ... 'finally:' construct. It could also be wrapped in a class where the dictionary iteration is self dispatched (a "private" method) and the appendix code follows that in the public method. (Understanding that the distension between public vs private is a matter of intention and documentation, not enforced by Python).
Another approach is to enumerate your dict and compare the current iteration against the final one. Its easier to look at and understand in my opinion:
for n, (key, value) in enumerate(yourDict.items()):
if yourDict[n] == yourDict[-1]:
print('Found the last iteration!:', n)
OR you could just do something once the iteration is finished:
for key, value in yourDict.items():
pass
else:
print('Finished iterating over `yourDict`')
No. When using an iterator you do not know anything about the position - actually, the iterator could be infinite.
Besides that, a dictionary is not ordered. So if you need it e.g. to insert commas between the elements you should take the items, sort them and them iterate over the list of (key, value) tuples. And when iterating over this list you can easily count the number of iterations and thus know when you have the last element.