Python Loop Iteration issue - python

Two versions, return opposite answers, but always one comes out wrong. I'm not sure where I've gone wrong. I've tried a series of other options, but this seems to get the closest. EDIT: Needs to be in a loop
goals: identify element in list, identify when element is not in list, identify when list is [], return strings accordingly.
def search_for_string(a_list, search_term):
i=0
for search_term in a_list:
i += 1
if a_list[i] == search_term:
return 'string found!'
elif a_list[i] != search_term:
return 'string not found2'
if len(a_list) == 0:
return 'string not found'
apple = search_for_string(['a', 'b', 'c'], 'd')
print(apple)
def search_for_string(a_list, search_term):
i=0
for search_term in a_list:
if a_list[i] == search_term:
return 'string found!'
elif a_list[i] != search_term:
return 'string not found2'
i += 1
if len(a_list) == 0:
return 'string not found'
apple = search_for_string(['a', 'b', 'c'], 'd')
print(apple)
other tests:
apple = search_for_string(['a', 'b', 'c'], 'b')
apple = search_for_string([], 'b')

Python makes your life super easy for this sort of thing:
def search_for_string(a_list, search_term):
if search_term in a_list:
return 'string found!'
return 'string not found'

There are few things wrong and Non-Pythonic in your code:
def search_for_string2(a_list, search_term):
i=0 # <----- Not Pythonic! If you want to get index we use enumerate(a_list)
for search_term in a_list: # <--- search_term passed to function is lost and gets overwritten by elements in a_list.
i += 1 # <--- Not Pythonic in this context
if a_list[i] == search_term: #<--- a_list[index+1] == a_list[index]. True if consecutive elements are same else False!
return 'string found!' #<--- No WRONG!, You didn't find the string, Consecutive elements are same!
elif a_list[i] != search_term:
return 'string not found2' #<-- Consecutive elements are not same!
if len(a_list) == 0:
return 'string not found'
According to the goals you have defined you can implement it like so:
def search_for_string(alist, search_term):
if not alist:
return "List is empty"
if search_term in alist:
return "First occurence of string Found at index position: " + str(alist.index(search_term))
else:
return "String not found"
print(search_for_string(['a', 'b', 'c'], 'd'))
print(search_for_string(['a', 'b', 'c'], 'b'))
print(search_for_string([], 'b'))
Output:
String not found
First occurence of string Found at index position: 1
List is empty

The short answer is that the return for != does not do what you think it does and that lists are 0-indexed not 1-indexed. The code is actually much simpler than you think:
def search_for_string(haystack, needle):
if not haystack: # check for empty list
return 'List was empty!'
for x in haystack:
if needle == x:
return 'String found!'
return 'String not found!'
Essentially, you only know if a string was not found if you’ve gone through and checked each element at least once. But you know if a string was found, well, when you find it.
Now to explain the issues with your code:
This version does not work because (1) it skips over the first element in the list and (2) it returns String not found/found only after checking the first element:
def search_for_string(a_list, search_term):
i=0
for search_term in a_list:
i += 1
if a_list[i] == search_term: # whoops this comparison checks for succeeding elements!
return 'string found!'
elif a_list[i] != search_term: # whoops this part returns even before all succeeding elements are checked.
return 'string not found2'
if len(a_list) == 0:
return 'string not found'
apple = search_for_string(['a', 'b', 'c'], 'd')
# In the list ['a', 'b', 'c']
# element [0] = 'a'
# element [1] = 'b'
# element [2] = 'c'
print(apple)
To explain a little further let’s take this step by step through your code:
# search_term == 'd'
# a_list = [ 'a', 'b', 'c' ]
i = 0 # at this point i == 0
for search_term in a_list:
# Oh no! we lost the search term that we passed into the
# function because we are using it as the loop iterator
# search_term == 'a'
i += 1 # i == 1
if a_list[i] == search_term:
# checks to see if 'b' == 'a'
return 'string found!'
elif a_list[i] != search_term:
# checks to see if 'b' != 'a'
return 'string not found!'
# and we return after one iteration of the loop.
Your second version has the same problem (1)(2), but avoids the issue where the first element is not checked.

There are lots of things wrong in your search_for_string function.
Main problem is that you're overwriting the value of the variable search_term. There are other problems as well that are causing incorrect output.
Here's a simpler version of your function and it meets all your requirements.
def search_for_string(a_list, search_item):
if(len(a_list) == 0):
return 'List is empty'
else:
for search_term in a_list:
if search_term == search_item:
return 'string found!'
return 'string not found'

You have quite a few errors in your in code. Some are important, others are not. I'll try to address them:
You're receiving the variable search_term as a function argument, but you then overwrite the value of it by using it in your for loop.
You're iterating over a_list by value, but you then attempt to use the loop variable i to iterate by index. Don't do this. You're already iterating by value, you don't need to do both.
You're trying to test if a_list is empty at the end of your function. Do at the beginning. Better yet, ditch the if statement and simply return at the end of your function. The for loop will not be run if a_list is empty.
Now, here is how I'd rewrite your function:
>>> def search_for_string(lst, key):
# only iterate by value.
for string in lst:
# we only need to test once
# if `key` is equal to the
# current string we are on.
if string == key:
return 'string found'
# no need to test if the list
# is empty. The for loop will
# never be run if it is, and
# this return statement will
# execute.
return 'string not found'
>>> search_for_string(['a', 'b', 'c'], 'd')
'string not found'
>>> search_for_string(['a', 'b', 'c'], 'b')
'string found'
>>> search_for_string([], 'b')
'string not found'
>>>

For your code, you should note that you're not searching properly. You pass in search_term, but a variable in a for x in y sets x to be equal to the value of the next item in y. So if you have for x in [1, 2, 3], the first time it runs it will set x = 1, etc. So first function will check if 'a' == 'b', which it's not, and the second function will check if 'a' == 'a', which it is--but neither is what you're looking for!
The best way to find if an item is in a list is
x in list
this will return True or False, if x is in the list or not! (don't use the variable 'list' though, it's bad practice since it shadows a built-in function).
So a more Pythonic way to do this would be
def search_for_string(a_list, search_term):
if search_term in a_list:
return 'string found!'
elif not a_list: # realistically you'd put this before here but I'm trying to mirror your code--why might you put this earlier? Because it's less costly than searching a list.
return 'empty list!'
else:
return 'string not found!'
Also note that bool([]) returns False, which is how we check if the list is empty.
To do it your way, we don't need to use index values, but you have to do a lot of extra, unnecessary work.
def search_for_string(a_list, search_term):
for index, item in enumerate(a_list):
if a_list[index] == search_term:
return 'string found!'
# what do you think the value of 'item' is here? it's equal to a_list[index]!
elif len(a_list) == 0: # again, you'd put this earlier--why make your computer do the work? it doesn't have to. Also, you could just do elif not a_list
return 'string not found'
else:
continue
return 'string not found2'

Most of the issues relevant to your code are covered in previous answers here, and the answer given by #Stephen Rauch sums up the most Pythonic approach to your problem.
There is one more thing that makes your code not do what you thing, even if all the other stuff was correct.
When you return in a function, you are effectively exiting that function.
So, effectively, using the for loop approach you've been trying, you would be only checking the first value in a_list, returning 'Found' if it met your search criteria, and returning 'Not found' if the first value did not match your search criteria, and then exiting your function.
Essentially, you'd never check beyond the first value.

First of all,
difference in your first method and second method are incrementing i before and after your if statement is executed. If you increment i first, your loop won't find the value for the first element of the list.
you are using i as an increment, but it is not necessary in python. You can just find out by using if the element is in a list.
def search_for_string(a_list, search_term):
#if a_list is empty, return False
if len(a_list) == 0:
return False
#if search_term has an element in a_list return the string
if search_term in a_list:
return "string found"
return "string not found"

Related

Return the number of times that the string "hi" appears anywhere in the given string -- Python codingbat problem

I'm aware that I can use the .count() function for this, but I tried to do this with a for loop and I keep getting a compile error in line 6. Does anyone see what's wrong with this code and why it wouldn't give the same output? Thanks in advance!
def count_hi(string):
# Create an empty list to add to
num_hi = []
# for every index in string , if the character is h and the next is i,
# add element to list
for index in string:
if string[index] == 'h' AND string[index + 1] == 'i':
num_hi.append('hi found')
return len(num_hi) # return length of list
Why not use count?
def count_hi(string):
return string.count('hi')
Fix for your code:
def count_hi(string):
count = 0
for i in range(1, len(string)):
if string[i - 1] == 'h' and string[i] == 'i':
count += 1
return count
Python is case sensitive, not AND but and.
Appending to list and counting it is a lot of overhead, you should just use a variable and increment it every time you find hi.
Instead of enumerate, you can use range to start from index 1 and check your string from i - 1 and 1. Avoids another check.
I'd prefer the previous solution using .count(). Why to write code when there is a built-in method for you?
See Harshal Parekh's Asnwer, it provides a better analysis and explanation.
When you iterate over a string, you're not iterating over indexes, but over the letters themselves. A quick fix for this could be:
def count_hi(string):
num_hi = []
# for every index in string , if the character is h and the next is i, add element to list
for index, _character in enumerate(string):
if index == len(string) - 1:
break # on the last one, you'd get an index error.
if string[index] == 'h' and string[index + 1] == 'i': # "and", not "AND"
num_hi.append('hi found')
return len(num_hi) # return length of list
The simplest way to do this is to use the builtin collection module.
from collections import Counter
def count_hi(string):
counts = Counter(string.split())
return print(counts['hi'])
You can try this, it should do the trick.
def count_hi(str):
counter = 0
str.lower()
str.replace(' ', '')
for i in range(0, len(str)-1):
if str[i] == 'h' and str[i+1] == 'i':
counter += 1
return counter
My solution:
def count_hi(str):
sum = 0
for i in range(len(str)-1):
if str[i:(i+2)] == "hi":
sum += 1
return sum

Comma Code program in Automate the Boring Stuff

I am trying to learn Python by working my way through 'Automate the Boring Stuff with Python'. I'm currently at chapter 4. The practice project I'm currently trying to build is as follows:
Say you have a list value like this:
spam = ['apples', 'bananas', 'tofu', 'cats']
Write a function that takes a list value as an argument and returns a string with all the items separated by a comma and a space, with and inserted before the last item. For example, passing the previous spam list to the function would return 'apples, bananas, tofu, and cats'. But your function should be able to work with any list value passed to it.
I have tried to find some answers online, but the programs online look very different from the one I have written. I was wondering how to fix my program. Currently it is only returning the first list item to me. Here is my code:
randomlist = []
while True:
print('Add something to the list: ')
listitem = input()
if listitem == '':
break
randomlist = randomlist + [listitem]
def commaplacer(somelist): #function
for i in range(len(somelist)): #this reiterates the list
if len(somelist)>i:
return somelist[i]
elif len(somelist)==i:
return 'and' + somelist[i]
else:
break
result = commaplacer(randomlist)
print(result)
When you return from a function, it means you don't want it to run any more. I think your return statements cause it to exit before you intend.
Instead, I think you should initialize your output as an empty string and then build it up as you move through the list. At the end of the loop, return output to send the completed string back.
I think your loop logic can be simplified to 2 cases. Either the item is the last in the list len(somelist)-1 == i and we need to add the 'and' or it is not the last item and we just need to put a comma.
Here's an example:
randomlist = ['Lions', 'tigers', 'bears, oh my!']
def commaplacer(somelist): # function
output = ''
for i in range(len(somelist)):
if len(somelist)-1 == i:
output += 'and ' + somelist[i]
else:
output += somelist[i] + ', '
return output
result = commaplacer(randomlist)
print(result)
will output
Lions, tigers, and bears, oh my!
You can consolidate the code to 3 lines in the function.
def commaplacer(somelist): #function
if not somelist: return ''
elif len(somelist) == 1: return somelist[0]
else: return ', '.join(somelist[:-1])+', and ' + somelist[-1]
#line 1: check if list is empty. If yes, return empty string
#line 2: check if list has only one value. If yes, return only first value
#line 3: since list has more than one value, use 'join' function
#join all values until last one. add ', and ' then add last value
Now you can call the function to get the desired result.
result = commaplacer(randomlist)
print(result)
The result will be:
#when you enter nothing, you get an empty string in return
>>> Add something to the list:
>>>
>>>
#when you enter only one value to the list
>>> Add something to the list:
>>> good
>>>
>>> good
#when you enter a few values to the list
>>> Add something to the list:
>>> apple
>>> Add something to the list:
>>> banana
>>> Add something to the list:
>>> carrot
>>> Add something to the list:
>>> egg
Add something to the list:
apple, banana, carrot, and egg
spam = []
def coma(listName):
output = ''
for i in range(len(listName):
if i==0:
output = listName[i]
elif i == len(listName) -1:
output += ', and ' + listName[i]
else:
output += ', ' + listName[i]
return output
print(coma(spam))

Palindrome recursive function

I tried to write a recursive function that says if a string is a palindrome, but all I get is an infinite loop and I don't know what the problem is
def isPalindrome(S):
listush=list(S) #listush=['a', 'b', 'n', 'n', 'b', 'a']
length=len(listush) #length=6
if length==0 or length==1:
return S, "is a palindrome!"
elif listush[0]!=listush[-1]:
return S, "is not a palindrome!"
else:
del listush[0]
del listush[-1]
return isPalindrome(S)
print isPalindrome("abnnba")
Hope this helps
def ispalindrome(word):
if len(word)<=1:
print("Palindrome")
return
else:
if word[0]!=word[-1]:
print("Not a palindrome")
return
return ispalindrome(word[1:len(word)-1])
word=input("Enter word ")
ispalindrome(word)
First of all, indent your code properly.
Secondly, you are calling the function again with the same argument. Call with 'listush' list from which you are deleting or delete from 'S' and recurse with S argument.
There's no need for creating a list. A python string is already an indexable sequence.
Even better, we can employ slicing and let the function return True and False instead of a tuple with text, With all of this, isPalindrome() becomes a one-liner:
def isPalindrome(S):
return len(S) < 2 or (S[0] == S[-1] and isPalindrome(S[1:-2]))
print isPalindrome('A')
>>> True
print isPalindrome('AA')
>>> True
print isPalindrome('BAAB')
>>> True
print isPalindrome('ABAB')
>>> False
There are some things I would like to say about your code
You can send a slice of the list, saving you the trouble of deleting
elements.
You don't need to convert it to a list, all the operations you need
in finding palindrome are supported by strings.
You are returning S in the recursive function, which would be an
empty list(or string) because it is diminishing each recursion. In
recursive cases, I suggest you to just return True or False
Here is an example.
def isPalindrome(S):
length=len(S)
if length < 2:
return True
elif S[0] != S[-1]:
return False
else:
return isPalindrome(S[1:length - 1])
Simple as that.
If you do an print(listush) you can see, that your list never changes.
The following modification of your code works:
def isPalindrome(testStr, orig=None):
if orig is None:
orig = testStr
length = len(testStr) #length=6
print(testStr)
if length == 0 or length == 1:
return orig, "is a palindrome!"
elif testStr[0] != testStr[-1]:
return orig, "is not a palindrome!"
else:
return isPalindrome(testStr[1:-1], orig)
print isPalindrome("abnnba")

Python: Recursive Function to check if string is a palindrome

So I'm trying to create a recursive function to check if a word is a palindrome. It should ignore all non-alphabetical characters. Here is what I have so far.
def is_palindrome(text):
'''
A Recursive Function that returns True if the parameter, text, is a palindrome, False if not.
Ignores capitalization, punctuation, and spaces.
text: a String
returns True or False
'''
text = list(text)
if len(text) == 0:
return True
else:
if text[0].isalpha():
if text[-1].isalpha():
if text[0].lower() == text[-1].lower():
text.remove(text[0])
text.remove(text[-1])
return is_palindrome(text)
else:
return False
else:
text.remove(text[-1])
return is_palindrome(text)
else:
text.remove(text[0])
return is_palindrome(text)
Here are some test cases...
is_palindrome("abcde")
Results
abcde
False
is_palindrome("aabbaa")
Results
aabbaa
['b', 'b', 'a', 'a']
False
is_palindrome("aa bb cc")
Results
aa bb aa
[' ', 'b', 'b', ' ', 'a', 'a']
['b', 'b', ' ', 'a', 'a']
False
So for some reason, it always directly ends up being false.
Thoughts on how to solve this? All help will be appreciated!
text.remove(text[0]) does not do what you think it does (It removes the first occurrence of that value from the list). To remove by index use slices. You can rewrite this:
text.remove(text[0])
text.remove(text[-1])
to this:
text = text[1:-1]
Is this just for fun, or an exercise? I don't think recursion really makes sense here:
def palindrome(s):
l = len(s)
m = l // 2
return s[:m][::-1] == s[m + (l % 2):]
Then I'd just preprocess the input to remove non alphanumerics and uppers like:
s = 'aBc%$cbA'
palindrome(re.subn('\W', '', s)[0].lower())
From the Python 3 documentation:
list.remove(x)
Remove the first item from the list whose value is x. It is an error if there is no such item.
When you call text.remove(text[-1]) for the list based on the string aabbaa, the first a in the list will be removed, which is not always the desired behavior. If you want to remove the first and last elements of the list, you can use a slice as user1434070 posted. An alternate method is to use list.pop(x) to remove the element with a specified index, which is accomplished by replacing the calls to remove with the following code:
text.pop(0)
if len(text) == 0:
return True
text.pop(-1)
Note that the size of the list must be checked after the first pop in case it contained a single letter. This occurs in the deepest level of recursion when the original palindrome contains an odd number of characters.
list.remove() removes the first occurrence of an element in the list. If that element is present at the beginning and end, it doesn't matter that you selected that element by indexing the list to get the last element - it'll still remove the first one it sees. list.pop() would be more appropriate, as that removes elements based on their index rather than their value.
def is_palindrome(text):
text = list(text)
if len(text) == 0:
return True
else:
if text[0].isalpha():
if text[-1].isalpha():
if text[0].lower() == text[-1].lower():
text.pop(0)
text.pop()
return is_palindrome(text)
else:
return False
else:
text.pop()
return is_palindrome(text)
else:
text.pop(0)
return is_palindrome(text)
However, you can further improve your program by filtering out any ignored characters at the beginning:
def is_palindrome(text):
text = ''.join(filter(str.isalpha, text)).lower()
if not text:
return True
text = list(text)
if text[0].lower() == text[-1].lower():
text.pop(0)
text.pop()
return is_palindrome(text)
return False
A further optimization would be to use a default argument to note whether the string has been cleaned already, and send a slice to the recursive call:
def is_palindrome(text, cleaned=False):
if not cleaned:
text = ''.join(filter(str.isalpha, text)).lower()
if not text:
return True
if text[0] != text[-1]:
return False
return is_palindrome(text[1:-1], 1)

checking next element in for loop

Let's say I have this array:
arr = ["foo","bar","hey"]
I can print the "foo" string with this code :
for word in arr:
if word == "foo":
print word
But I want also check the next word if it equals to "bar" then it should print "foobar"
How can I check the next element in a for loop?
The items in a list can be referred to by their index. Using the enumerate() method to receive an iterator along with each list element (preferable to the C pattern of instantiating and incrementing your own), you can do so like this:
arr = ["foo","bar","hey"]
for i, word in enumerate(arr):
if word == "foo" and arr[i+1] == 'bar':
print word
However, when you get to the end of the list, you will encounter an IndexError that needs to be handled, or you can get the length (len()) of the list first and ensure i < max.
for i in range(len(arr)):
if arr[i] == "foo":
if arr[i+1] == 'bar':
print arr[i] + arr[i+1]
else:
print arr[i]
Rather than check for the next value, track the previous:
last = None
for word in arr:
if word == "foo":
print word
if last == 'foo' and word == 'bar':
print 'foobar'
last = word
Tracking what you already passed is easier than peeking ahead.
You can also do it with zip:
for cur, next in zip(arr, arr[1:]):
if nxt=='bar':
print cur+nxt
But keep in mind that the number of iterations will be only two since len(ar[1:]) will be 2

Categories

Resources