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.
Related
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"
I recently received an answer from the stackoverflow fellow on my previous question and I tried to inquire more in order to understand the function but somehow no response so I wish to ask it here.
I wanted to know what is the k and v that used in the lambda represent? I thought it was representing like this......
k = dictionary ?
v = string ? # Did I understand it correctly?
dictionary = {"test":"1", "card":"2"}
string = "There istest at the cardboards"
from functools import reduce
res = reduce(lambda k, v: k.replace(v, dictionary[v]), dictionary, string)
since we use lambda then it loop each of the element within both of these variables. But why k.replace? Isnt that a dictionary? Should It be v.replace? Somehow this method works. I wish someone could explain to me how this work and please more details if possible. Thank you!
reduce is equivalent to repeatedly calling a function.
The function in this case is a lambda, but a lambda is just an anonymous function:
def f(k, v):
return k.replace(v, dictionary[v])
The definition of reduce itself is (almost—the None default here is not quite right, nor the len test):
def reduce(func, seq, initial=None):
if initial is not None:
ret = initial
for i in seq:
ret = func(ret, i)
return ret
# initial not supplied, so sequence must be non-empty
if len(seq) == 0:
raise TypeError("reduce() of empty sequence with no initial value")
first = True
for i in seq:
if first:
ret = i
first = False
else:
ret = func(ret, i)
return ret
So, ask yourself what this would do when called on your lambda function. The:
for i in dictionary
loop will iterate over each key in the dictionary. It will pass that key, along with the stored ret (or the initial argument for the first call), to your function. So you'll get each key, plus the string value that's initially "There istest at the cardboards", as your v (key from dictionary, called i in the expansion of reduce) and k (long string, called ret in the expansion of reduce) arguments.
Note that k is the full text string, not the string used as the key in the dictionary, while v is the word that is the key in the dictionary. I've used the variable names k and v here only because you did too. As noted in a comment, text and word might be better variable names in either the expanded def f(...) or the original lambda function.
Trace your code execution
Try the same code, except that instead of just:
def f(k, v):
return k.replace(v, dictionary[v])
you write it as:
def f(text, word):
print("f(text={!r}, word={!r})".format(text, word))
replacement = dictionary[word]
print(" I will now replace {!r} with {!r}".format(word, replacement))
result = text.replace(word, replacement)
print(" I got: {!r}".format(result))
return result
Run the functools.reduce function over function f with dictionary and string as the other two arguments and observe the output.
I have a function doing binary tree traversal, and I have a global list "stack" to store temporary values.
stack = []
def search(root, v):
global stack
stack.append(root.info)
if root.info == v:
print(stack) #<------- THE FIRST PRINT IS HERE
return
if root.left is not None:
search(root.left, v)
if root.right is not None:
search(root.right, v)
stack.pop()
pass
def lca(root, v1, v2):
search(root,v1)
print(stack) #<------- THE SECOND PRINT IS HERE
Your Output (stdout)
[4, 2, 1]
[4]
input: v1=1, v2=7
When I print the value of the list from inside and outside the function, I found that the results are different -- when printed inside, the result is [4,2,1], and outside is [4]. I have tried different ways, such as creating the list outside the function, and then pass it to the function, the result is always the same. Can anyone know why this happened?
You seem to believe that after you hit the explicit return in your search function, your recursive search stops. But it doesn't. The search calls higher up the call stack will still go on triggering more searches, popping things off stack, altering its contents.
Perhaps you want something like this: use a return value to signal from search when it has successfully found the thing it was looking for, and don't search any more after that happens.
def search(root, v):
global stack
stack.append(root.info)
if root.info == v:
print(stack)
return True
if root.left is not None and search(root.left, v):
return True
if root.right is not None and search(root.right, v):
return True
stack.pop()
return False
This will prevent the stack being altered after the point where the value is found.
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()
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.