Problem:
Write a program that will search a list to find the first odd number. If an odd number is found, then find the first even number following the odd number. Return the distance between the first odd number and the first even number. Return -1 if no odd numbers are found or there are no even numbers following an odd number.
My Code:
def go(list1):
dist = 0
odd = 0
even = 0
for i in range(0,len(list1)):
if list1[i] % 2 == 1:
odd = list1[i]
break
else:
odd = list1[0]
list2 = list1[list1.index(odd)+1:]
for i in range(0,len(list2)):
if list2[i] % 2 == 0:
even = list2[i]
break
else:
even = list2[0]
return list2.index(even) + list1.index(odd) + 1 - list1.index(odd)
print(go([7,1,5,3,11,5,6,7,8,9,10,12345,11]))
print(go([11,9,8,7,6,5,4,3,2,1,-99,7]))
print(go([10,20,30,40,5,41,31,20,11,7]))
print(go([32767,70,4,5,6,7]))
print(go([2,7,11,21,5,7]))
print(go([7,255,11,255,100,3,2]))
print(go([9,11,11,11,7,1000,3]))
print(go([7,7,7,11,2,7,7,11,11,2]))
print(go([2,4,6,8,8]))
My Output:
6
2
3
1
1
4
5
4
1
Desired Output:
6
2
3
1
-1
4
5
4
-1
What am I doing wrong? Is there a better approach to this problem than what I have done?
6 Answers 6
You could approach this with an iterator.
An iterator is an object that "remembers" its current position in the list. When the iterator is created, it points to the first element in the list. You can then move the iterator forward with the next function.
So the idea is this:
- Create an iterator
- Move the iterator forward until you find the first odd number
- Move it further forward until you find an even number, counting the steps
In step 3, the enumerate function is very useful for counting how many elements the iterator has skipped.
def go(iterable):
# step 1: get an iterator for this iterable
itr = iter(iterable)
try:
# step 2: advance the iterator to the first odd number
next(num for num in itr if num % 2 == 1)
# step 3: count the elements up to the next even number
return next(i for i, num in enumerate(itr, 1) if num % 2 == 0)
except StopIteration:
# StopIteration is raised if the iterator reaches the end without
# finding a suitable number
return -1
1 Comment
Your errors are:
- If you don't find the even or the odd number you take the first element instead of returning -1.
- to compute the distance, you must not subtract
list1.index(odd)(just remove this part and the equation is correct)
Instead of storing the values and creating a new list, you should store the positions:
def go(list1):
odd = None
even = None
for i in range(0,len(list1)):
if list1[i] % 2 == 1:
odd = i
break
if odd is not None:
for i in range(odd, len(list1)):
if list1[i] % 2 == 0:
even = i
break
if odd is None or even is None:
return -1
else:
return even - odd
And here's a more pythonic version:
def go(list1):
try:
odd = next(i for (i, v) in enumerate(list1) if v % 2 == 1)
even = next(i for (i, v) in enumerate(list1) if v % 2 == 0 and i > odd)
return even - odd
except StopIteration:
return -1
StopIteration is an exception raised when next reaches the end of the list without any matching value.
2 Comments
StopIteration in the answer (for the sake of new learners of the language).Answering what is wrong I think it the use of your 'else ' statements:
else:
even = list2[0]
Why are you assigning a number from the list if you do not find any even? That is why your distance is = 1
Same thing for :
else:
odd = list1[0]
You shouldn't assign a number to odd, if you do not find one.
Comments
I would do something like this:
def go(list1):
dist = 0
found = False;
for i in range(0,len(list1)):
if list1[i] % 2 == 1:
for j in range(i,len(list1)):
if list1[j] % 2 == 0:
found = True;
return(j-i)
break
if(found == False):
return -1;
print(go([7,1,5,3,11,5,6,7,8,9,10,12345,11]))
print(go([11,9,8,7,6,5,4,3,2,1,-99,7]))
print(go([10,20,30,40,5,41,31,20,11,7]))
print(go([32767,70,4,5,6,7]))
print(go([2,7,11,21,5,7]))
print(go([7,255,11,255,100,3,2]))
print(go([9,11,11,11,7,1000,3]))
print(go([7,7,7,11,2,7,7,11,11,2]))
print(go([2,4,6,8,8]))
The main problem in your code I see is that you default to the first element in the list instead of defaulting to -1 if odd or even isn't found. You never check to see if its not found.
I added a boolean in my code that checks if we have found the element yet. The code works without the boolean altogether but I added it to show that we want to be checking to see if we have found the odd and even element yet or not. If we don't find it: we return -1. Otherwise: we return the difference.
Comments
This is similar to what other commenters have suggested by storing the index of the even and odd numbers:
def go(list1):
dist = 0
odd_idx = None
even_idx = None
for i in range(len(list1)):
if list1[i] % 2 == 1:
odd_idx = i
break
if odd_idx is None:
return -1
list2 = list1[odd_idx+1:]
for i in range(len(list2)):
if list2[i] % 2 == 0:
even_idx = i
break
if even_idx is None:
return -1
return abs(even_idx - odd_idx + 1)
print(go([7,1,5,3,11,5,6,7,8,9,10,12345,11]))
print(go([11,9,8,7,6,5,4,3,2,1,-99,7]))
print(go([10,20,30,40,5,41,31,20,11,7]))
print(go([32767,70,4,5,6,7]))
print(go([2,7,11,21,5,7]))
print(go([7,255,11,255,100,3,2]))
print(go([9,11,11,11,7,1000,3]))
print(go([7,7,7,11,2,7,7,11,11,2]))
Hope this helps!
3 Comments
5 and the next even number 20 is 3. The test looks correct.if odd_idx is None and if even_idx is None by else in both cases (to do a for-else).There are multiple mistakes in your code, that together contribute to the fact that your code will never return -1. It will even throw ValueError in case you pass in an empty list.
The first problem is that you assign odd = list1[0] in case you don't find an odd number. This is wrong in the case when there is no odd number. Then list2 will contain everything apart from the first number in list1. Same goes, for assigning even = list2[0] in case there is no even number after the first odd one.
Your function is correct in case there is a pair of numbers you are looking for. Though, your return statement can be simplified to list2.index(even)+1.
Also, in python you can loop over a list using for x in lst statement. In case you want to have access to the index of the element you are currently looking at use enumerate like this
for i, x in enumerate(lst)
Finally, here is a nicest simple way to solve your problem I can think of that requires only one iteration of the list.
def go(lst):
odd_index = -1 # Index of the first odd number in the list
for i, n in enumerate(lst):
if n % 2 == 1:
# n is odd
if odd_index == -1:
# This is the first even number we found, so we assign
# its index to odd_index and look for an even number
odd_index = i
else:
# n is even
if odd_index != -1:
# We already found odd number, so this is the
# first even number and we can return
return i - odd_index
# The search was not successful, so we return -1
return -1
-1. Do you think you wrote any handling for the cases where you're supposed to return-1?oddwould get set to the value of the last item instead of some value to indicate that it was not found.oddvariable to an even value in the array? In my opinion, it makes more sense to setodd = -1initially, then check if it is still-1after that loop.return list2.index(even) + list1.index(odd) + 1 - list1.index(odd), through algebra we can simplify this toreturn list2.index(even) + 1, which does not make sense w.r.t. the goal condition of the distance between the first odd number and the first even number.