JenkinsMa
JenkinsMa

Reputation: 225

Python - List Manipulation Exercise

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?

Upvotes: 3

Views: 708

Answers (6)

Michal Bock
Michal Bock

Reputation: 89

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

Upvotes: 0

Aran-Fey
Aran-Fey

Reputation: 43146

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

Upvotes: 5

Maxime Chéramy
Maxime Chéramy

Reputation: 18821

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.

Upvotes: 4

Benjamin Lee
Benjamin Lee

Reputation: 501

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!

Upvotes: 0

chevybow
chevybow

Reputation: 11878

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.

Upvotes: 0

daniel_hck
daniel_hck

Reputation: 1140

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.

Upvotes: 0

Related Questions