Python3: for-loop break and else (if statement) - python

Background information:
hey,
I want to do the following: I have a dictionary with IDs as keys and lists with various things as value. One of the items of the value is a string. I want to check, if a list contains this string. And I want to do it for all keys in my dictionary.
If the list contains the string, I want to print "String is valid"
If the list does not contain the string, I want to print "String is NOT valid"
So far, so good.
Furthermore, the lists I want to check depend on one console input of the user, which specifies, which list should be checked. The console input is "number".
My idea was to iterate over my dictionary and my list with a nested for-loop and compare, if the string(the item of the value) is equal to any list item. If it is, I want to break out of the loop. If the String is not found in the list, I want to execute the else-statement to print my "String is not valid" message.
Code snippet:
def validationHelper(myDict, myList):
for key in myDict:
for value in myDict[key][0]:
for item in myList:
if value==item:
validationHelper.true="String is valid"
break
else:
validationHelper.true="Warning: String is NOT valid"
def validation(anyList,helperfunc):
if anyList=="one":
return helperfunc(finalDict,myList1)
if anyList=="two":
return helperfunc(finalDict,myList2)
if anyList=="three":
return helperfunc(finalDict,myList3)
validation(number, validationHelper)
print(validationHelper.true)
Problem:
I am running this, but no matter if the string is in the list or not, I always get my printout for the else-statement. So, I guess I have an error in reasoning in my for-loop? Or maybe, I did not understand for-loops at all?! I have tried out different indentions with the else-statement, but couldnt solve my problem.

I would suggest you to change your function the following way (without changing the logic):
def validationHelper(myDict, myList):
for key in myDict:
for value in myDict[key][0]:
for item in myList:
if value==item:
return "String is valid" # Add here to exit
return "Warning: String is NOT valid" # will be returned inf nothing will be found in your 3 loops
def validation(anyList,helperfunc):
if anyList=="one":
return helperfunc(finalDict,myList1)
if anyList=="two":
return helperfunc(finalDict,myList2)
if anyList=="three":
return helperfunc(finalDict,myList3)
validation(number, validationHelper)
print(validationHelper)
This will help you to exit your 3 nested loops as it was mentioned in comments.
Because in the negative case on first wrong occurrence you don't need to check anything else.

Use return to break all of your loop. Having an else statement is not necessary if you don't have any if statement to begin with.
def validationHelper(myDict, myList):
for item in myList:
if item in myDict.values():
return ("String is valid")
return ("String is NOT valid")
def validation(anyList,helperfunc):
if anyList=="one":
return helperfunc(finalDict,myList1)
elif anyList=="two":
return helperfunc(finalDict,myList2)
elif anyList=="three":
return helperfunc(finalDict,myList3)
validation(number, validationHelper)
print(validationHelper.true)
Using elif instead of multiple if is a better practice. Be careful with indentions next time.
Also you might want to check .keys() and .values()
You can replace:
for key in myDict:
for value in myDict[key][0]:
with:
for value in myDict.values():

The other answers give a good explanation of how to break out of multiple loops. But you could also simplify your code by using Python's built-in functions and list comprehensions, like this:
def validationHelper(myDict, myList):
if any(v in myList for val in myDict.values() for v in val[0]):
validationHelper.true="String is valid"
else:
validationHelper.true="Warning: String is NOT valid"
def validation(anyList,helperfunc):
if anyList=="one":
return helperfunc(finalDict,myList1)
if anyList=="two":
return helperfunc(finalDict,myList2)
if anyList=="three":
return helperfunc(finalDict,myList3)
validation(number, validationHelper)
print(validationHelper.true)
This should be as efficient as your code, since any short circuits at the first match. And it may be a little more readable. (Note that multi-level list comprehensions go in the same order as regular for loops.)

Related

Comparing items through a tuple in Python

I am given an assignment when I am supposed to define a function that returns the second element of a tuple if the first element of a tuple matches with the argument of a function.
Specifically, let's say that I have a list of student registration numbers that goes by:
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
And I have defined a function that is supposed to take in the argument of reg_num, such as "S12345", and return the name of the student in this case, "John". If the number does not match at all, I need to print "Not found" as a message. In essence, I understand that I need to sort through the larger tuple, and compare the first element [0] of each smaller tuple, then return the [1] entry of each smaller tuple. Here's what I have in mind:
def get_student_name(reg_num, particulars):
for i in records:
if reg_num == particulars[::1][0]:
return particulars[i][1]
else:
print("Not found")
I know I'm wrong, but I can't tell why. I'm not well acquainted with how to sort through a tuple. Can anyone offer some advice, especially in syntax? Thank you very much!
When you write for i in particulars, in each iteration i is an item of the collection and not an index. As such you cannot do particulars[i] (and there is no need - as you already have the item). In addition, remove the else statement so to not print for every item that doesn't match condition:
def get_student_name(reg_num, particulars):
for i in particulars:
if reg_num == i[0]:
return i[1]
print("Not found")
If you would want to iterate using indices you could do (but less nice):
for i in range(len(particulars)):
if reg_num == particulars[i][0]:
return particulars[i][1]
Another approach, provided to help learn new tricks for manipulating python data structures:
You can turn you tuple of tuples:
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
into a dictionary:
>>> pdict = dict(particulars)
>>> pdict
{'S12345': 'John', 'S23456': 'Max', 'S34567': 'Mary'}
You can look up the value by supplying the key:
>>> r = 'S23456'
>>> dict(pdict)[r]
'Max'
The function:
def get_student_name(reg, s_data):
try:
return dict(s_data)[reg]
except:
return "Not Found"
The use of try ... except will catch errors and just return Not Found in the case where the reg is not in the tuple in the first place. It will also catch of the supplied tuple is not a series of PAIRS, and thus cannot be converted the way you expect.
You can read more about exceptions: the basics and the docs to learn how to respond differently to different types of error.
for loops in python
Gilad Green already answered your question with a way to fix your code and a quick explanation on for loops.
Here are five loops that do more or less the same thing; I invite you to try them out.
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
for t in particulars:
print("{} {}".format(t[0], t[1]))
for i in range(len(particulars)):
print("{}: {} {}".format(i, particulars[i][0], particulars[i][1]))
for i, t in enumerate(particulars):
print("{}: {} {}".format(i, t[0], t[1]))
for reg_value, student_name in particulars:
print("{} {}".format(reg_value, student_name))
for i, (reg_value, student_name) in enumerate(particulars):
print("{}: {} {}".format(i, reg_value, student_name))
Using dictionaries instead of lists
Most importantly, I would like to add that using an unsorted list to store your student records is not the most efficient way.
If you sort the list and maintain it in sorted order, then you can use binary search to search for reg_num much faster than browsing the list one item at a time. Think of this: when you need to look up a word in a dictionary, do you read all words one by one, starting by "aah", "aback", "abaft", "abandon", etc.? No; first, you open the dictionary somewhere in the middle; you compare the words on that page with your word; then you open it again to another page; compare again; every time you do that, the number of candidate pages diminishes greatly, and so you can find your word among 300,000 other words in a very small time.
Instead of using a sorted list with binary search, you could use another data structure, for instance a binary search tree or a hash table.
But, wait! Python already does that very easily!
There is a data structure in python called a dictionary. See the documentation on dictionaries. This structure is perfectly adapted to most situations where you have keys associated to values. Here the key is the reg_number, and the value is the student name.
You can define a dictionary directly:
particulars = {'S12345': 'John', 'S23456': 'Max', 'S34567': 'Mary'}
Or you can convert your list of tuples to a dictionary:
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
particulars_as_dict = dict(particulars)
Then you can check if an reg_number is in the dictionary, with they keyword in; you can return the student name using square brackets or with the method get:
>>> particulars = {'S12345': 'John', 'S23456': 'Max', 'S34567': 'Mary'}
>>> 'S23456' in particulars
True
>>> 'S98765' in particulars
False
>>>
>>> particulars['S23456']
'Max'
>>> particulars.get('S23456')
'Max'
>>> particulars.get('S23456', 'not found')
'Max'
>>>
>>> particulars['S98765']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'S98765'
>>> particulars.get('S98765')
None
>>> particulars.get('S98765', 'not found')
'not found'

Is there a way to search by key in a dictioanry in a list, even when you're not sure where the dictionary is?

I have a weird json file to work with. I'm trying to find the key dt in a dictionary in a list. The problem is that sometimes it can be something like a dictionary in a list in a list or a dictionary in a dictionary or a list.
The "dt" key exists, but the position isn't guaranteed. Is there a way for me to get this key? At first, I tried using many if and for statements, but I realized it would be too much.
I then tried converting the json file to a string and using re.search to search for {'dt':, but I wasn't sure about that accuracy. Is there any way to search for the "dt" key without knowing the exact position of the dictionary? Thanks!
Is this what you were looking for ? Please note that I did not check all the use cases because I am not aware of all of them. Think it should cover all of them , but please validate. The code can be improved so much - this is just an initial version, hope you can improve on it :)
funcIter is a function that gets called over and over until dt is found. It checks if the input is of type dictionary and then iterates over the dictionary object to find the key. If it is of any other type if assumes it is of type list (you can add one more check to check specifically for type list) and then grabs the first item.
dicObject = {"value" :[{"dt":12345}]}
def funcIter(items):
if(isinstance(items, dict)):
for key, value in items.items():
if key.startswith('dt'):
print (key, value)
else:
funcIter(value)
else:
indexes = [n for n, x in enumerate(items) if x == 'dt']
if(len(indexes) < 1):
funcIter(items[0])
else:
print(items[0])
pass
funcIter(dicObject)
I finally did it! All that was needed was a recursive function. Below is the functioning recursive function.
def findDT(givenThing, key):
if isinstance(givenThing, dict):
for a in givenThing.keys():
if a == key:
print(givenThing[key])
return givenThing[key]
else:
findDT(givenThing[a], key)
elif isinstance(givenThing, list):
for a in givenThing:
if isinstance(a, list) or isinstance(a, dict):
givenThing = a
findDT(givenThing, key)

Dictionary Output Issue

I am trying to understand my below code a little better as far as why certain output is occurring.
stuff = {'purple':[0, 106, 506, 'SI', 'Lauren'], 'blue':'Cornflower', 'yo':'green'}
stuff_keys_sorted = sorted(stuff.keys())
print sorted(stuff.keys())
for k in stuff_keys_sorted:
if type(stuff[k]) == type(['hello', 'goodbye']):
for item in stuff[k]:
print item
print k
The current output is:
0
106
506
SI
Lauren
yo
I understand why everything is happening up to the last line of " yo". Why would "yo" be the only option to print out, shouldn't my code just print any key in the dictionary?
You put the print k statement outside of the loop. The for loop is finished by the time Python reaches that statement, and only the last value for k is then printed.
If you wanted to print each key, you'd need to make it part of the loop:
for k in stuff_keys_sorted:
# ...
print k
Some other notes on your code:
You don't have to call .keys(), stuff_keys_sorted = sorted(stuff) is enough to get a sorted sequence of dictionary keys.
To test for a specific type, use isinstance() rather than using type(..) == type(..):
if isinstance(stuff[k], list):
Even if you did need to use type(), you wouldn't need a list with contents; type([]) would have been sufficient. But so would using type(..) is list (as the outcome of type([]) is be list, and there is only one copy of every Python built-in type so using is is going to be a faster test).

update string from a dictionary with the values from matching keys

def endcode(msg,secret_d):
for ch in msg:
for key,value in secret_d:
if ch == key:
msg[ch] = value
return msg
encode('CAN YOU READ THIS',{'A':'4','E':'3','T':'7','I':'1','S':'5'})
This is my code. What I am trying to do here is for every characters in a string msg, the function should search in the dictionary and replace it with the mapping string if the character ch is a key in the dictionary secret_d.
If ch is not a key in secret_d than keep it unchanged.
For the example, the final result is should be 'C4N YOU R34D 7H15'
Your function name is endcode but you are calling encode.
But more important, I'll give you a hint to what's going on. This isn't going to totally work, but it's going to get you back on track.
def endcode(msg,secret_d):
newstr=""
for ch in msg:
for key,value in secret_d.iteritems():
if ch == key:
newstr=newstr+value
print(msg)
endcode('CAN YOU READ THIS',{'A':'4','E':'3','T':'7','I':'1','S':'5'})
But if you want a complete answer, here is mine.
A few issues:
As rb612 pointed out, there's a typo in your function definition ("endcode")
you are doing nothing with the return value of your function after calling it
msg[ch] is trying to assign items in a string, but that's not possible, strings are immutable. You'll have to build a new string. You cannot "update" it.
in order to iterate over (key, value) pairs of a dictionary d, you must iterate over d.items(). Iterating over d will iterate over the keys only.
That being said, here's my suggestion how to write this:
>>> def encode(msg, replacers):
... return ''.join([replacers.get(c, c) for c in msg])
...
>>> result = encode('CAN YOU READ THIS',{'A':'4','E':'3','T':'7','I':'1','S':'5'})
>>> result
'C4N YOU R34D 7H15'
Things to note:
dict.get can be called with a fallback value as the second argument. I'm telling it to just return the current character if it cannot be found within the dictionary.
I'm using a list comprehension as the argument for str.join instead of a generator expression for performance reasons, here's an excellent explanation.

Solving nested dict iteration by list comprehension or any other method

I have a dict containing another dict inside it
d1 = {'a':{'p':1, 'q':2, 'r':'abc'},
'b':{'p':5, 'q':6, 'r':["google", "pypi.org"]}
}
url1 = "https://google.com"
url2 = "https://abc.com"
Now what I want to do is run a check on values of r from both the dict values but I don't want any code redundancy.How is that possible ?
What I am doing right now is :-
for k, v in d1.iteritems():
if isinstance(v['r'], list):
for l in v['r']:
if url1.find(l):
..Do something..
else:
continue
else:
if url1.find(v['r'):
..Do Something same as above..
else:
continue
Now the problem arises with the same Do something repeated 2 times , Is there a way to solve redundancy with comprehension or by any other method , except function making and calling .
Edit-- The code is already inside a large function definition , so do provide other solutions than making another function and calling it.
You can convert the non-list items, ie. string in this case to a list and then simply iterate over that list. And you don't need that else: continue part:
for k, v in d1.iteritems():
value = v['r'] if isinstance(v['r'], list) else [v['r']]
for l in value:
if url1.find(l):
#Do something..
If you are dead serious about performance in python code and are willing to accept certain stylistic compromises, the following form will run just as fast as the manually inlined code, assuming you use pypy:
def inner():
pass
for k, v in d1.items():
if isinstance(v['r'], list):
for l in v['r']:
if url1.find(l):
inner()
else:
continue
else:
if url1.find(v['r']):
inner()
else:
continue
For a slightly more realistic example including non-empty inner and some timing code, please see this link.
Note that, just as you wrote, this version is significantly slower than the inlined one under CPython, which of course does no JIT inlining.
Freakish is right. Using a functions will be the best solution here. The overhead of using function calls will be negligible and it will probably be less than creating new lists and looping over lists of length one.
However, if you do want to avoid code duplication at all costs and avoid multiple function calls, you may want to consider rewriting your code to a generator function. This will yield the items you want to process one at a time.
def loop(d1):
for k, v in d1.iteritems():
if isinstance(v['r'], list):
for l in v['r']:
if url1.find(l):
yield l
else:
continue
else:
if url1.find(v['r']):
yield v['r']
else:
continue
for item in loop(d1):
print "do something"

Categories