FAQ: Code Challenge: Loops - Delete Starting Even Numbers

2 posts were merged into an existing topic: Why doesn’t iterating with for work while removing items from a list?

The general idea is to find the first ODD number in the list, and make it the first element of the returned list.

7 Likes

16 posts were split to a new topic: Code challenge lists - improving my code

8 posts were split to a new topic: Struggles [solved]

14 posts were split to a new topic: Code challenge lists - Accounting for an all even list?

2 posts were split to a new topic: List index out of range

2 posts were merged into an existing topic: Why doesn’t iterating with for work while removing items from a list?

6 posts were split to a new topic: Why doesn’t slicing an empty list throw an error?

15 posts were merged into an existing topic: Why doesn’t iterating with for work while removing items from a list?

For anybody exploring the multitude of possible solutions for this problem, here is another one to add to the pile…

try/except with `next()`
>>> def delete_starting_evens(lst):
	try:
		return lst[lst.index(next(x for x in lst if x % 2)):]
	except StopIteration:
		return []

	
>>> a = [10,2,30,4,51,60,75]
>>> b = [10,2,30,4,52,60,76]
>>> delete_starting_evens(a)
[51, 60, 75]
>>> delete_starting_evens(b)
[]
>>> 

It breaks down like this,

breakdown
>>> try:
	y = next(x for x in a if x % 2)
	z = a.index(y)
	print (a[z:])
except StopIteration:
	print ([])

	
[51, 60, 75]
>>> try:
	y = next(x for x in b if x % 2)
	z = b.index(y)
	print (b[z:])
except StopIteration:
	print ([])

	
[]
>>> 

Some things above may not yet have surfaced in this course:

  1. list.index()
  2. Handling exceptions with try...except.
  3. The next() function

That being the case, one may wish to cycle back to this solution when those topics come up or on review after completing the course.

2 Likes

Hm. I’m not sure I agree with both using an iterator and then going back to the start to search for the index - I would rather only consume from the iterator, and anything that has been dropped can be forgotten.

If there’s a rope sticking out of a wall (an iterator), then I could pull at it until there’s a non-even value, and then either turn the rest into a list, or let whoever wants the remaining values pull out the rest of the rope when and if they want it.

Sidenote:
Almost cheating, but interesting enough, and possibly not immediately obvious even knowing it exists - itertools.dropwhile implements a more general version of this problem: dropwhile(even, lst)

2 Likes

itertools.dropwhile

Interesting. Thanks for that.

One could use range to expose the indices.

with `range()`
>>> def delete_starting_evens(lst):
	try:
		return lst[next(i for i in range(len(lst)) if lst[i] % 2):]
	except StopIteration:
		return []

	
>>> delete_starting_evens(a)
[51, 60, 75]
>>> delete_starting_evens(b)
[]
>>> 

Or, use enumerate to expose both index and value…

with `enumerate()`
>>> def delete_starting_evens(lst):
	try:
		return lst[next(i for i, x in enumerate(lst) if x % 2):]
	except StopIteration:
		return []

	
>>> delete_starting_evens(a)
[51, 60, 75]
>>> delete_starting_evens(b)
[]
>>> 

Either of which make more sense than the earlier example given your advice.

1 Like

That’s a smaller amount of loops.
If you’re going to do list slicing, I’d really like to see finding the start location as a separate step (separate line, put result in variable)
And then, if you’re going to use iterators as a strategy of finding the first value - fine.

I'm looking for something more like this for using iterators
def delete_starting_evens(xs):
    xs = iter(xs)
    for x in xs:
        if x % 2 != 0:
            yield x
            yield from xs
            return
Or this, using find and slice
def delete_starting_evens(xs):
    for i, x in enumerate(xs):
        if x % 2 != 0:
            return xs[i:]
    return []
Or after abstracting out some stuff - if the language doesn't come with the kind of words to describe what's wanted then putting it all in one function may make it a bit difficult to read. Defining the words you want allows for expressing things in nicer ways.
def even(n):
    return n % 2 == 0


def not_(f):
    def f_(*args, **kwargs):
        return not f(*args, **kwargs)
    return f_


def find(f, xs):
    for i, x in enumerate(xs):
        if f(x):
            return i
    return i+1


def delete_starting_evens(xs):
    first_non_even = find(not_(even), xs)
    return xs[first_non_even:]
But obviously the right word here already exists in dropwhile, to the point where this whole function shouldn't exist at all, dropwhile covers it.
from itertools import dropwhile


def even(n):
    return n % 2 == 0


def delete_starting_evens(xs):
    return dropwhile(even, xs)
4 Likes

The second example is how I originally solved this one. Love going down the rabbit hole and seeing what pops up.

Thanks again for the great examples; and, I won’t argue with your last point. It makes the most sense. You’d think I had an aversion to libraries, or something the way I never reach for them.

2 Likes

A post was merged into an existing topic: Why doesn’t iterating with for work while removing items from a list?

27 posts were merged into an existing topic: Why doesn’t iterating with for work while removing items from a list?

3 posts were split to a new topic: Infinite loop [solved]

4 posts were split to a new topic: How does break work? [solved]

8 posts were split to a new topic: Code challenge lists - improving my solution [solved]

3 posts were split to a new topic: Using continue in a while loop [solved]