3

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?

5
  • 2
    Well, on one hand, props for actually showing runnable code, actual output, and desired output. On the other hand, the differences between your output and the desired output are cases where the desired output is -1. Do you think you wrote any handling for the cases where you're supposed to return -1? Commented May 7, 2018 at 16:49
  • @user2357112 I have tried in the else: statement to return -1, but that doesn't give the desired output. Commented May 7, 2018 at 16:51
  • Think about how your logic will work if no odd numbers are found. Specifically in your first loop, odd would get set to the value of the last item instead of some value to indicate that it was not found. Commented May 7, 2018 at 16:54
  • In your first loop, I don't think your else statement is very helpful. Why would you want to set your odd variable to an even value in the array? In my opinion, it makes more sense to set odd = -1 initially, then check if it is still -1 after that loop. Commented May 7, 2018 at 16:56
  • Also in return list2.index(even) + list1.index(odd) + 1 - list1.index(odd), through algebra we can simplify this to return 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. Commented May 7, 2018 at 16:56

6 Answers 6

5

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:

  1. Create an iterator
  2. Move the iterator forward until you find the first odd number
  3. 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
Sign up to request clarification or add additional context in comments.

1 Comment

Good idea using an iterator. Clean answer. +1
4

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

The Pythonic version is nice and clean! +1. But I think you should explain why you are catching StopIteration in the answer (for the sake of new learners of the language).
@KeyurPotdar explanation added
0

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

0

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

0

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

Regarding that test case, the dist between the first odd number 5 and the next even number 20 is 3. The test looks correct.
Good catch! My mistake. Edited the post.
btw, I tend to avoid to do it because it's controversial but you could replace if odd_idx is None and if even_idx is None by else in both cases (to do a for-else).
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.