Why doesn't iterating with `for` work while removing items from a list?

here is my workaround:

def delete_starting_evens(lst):
  
  if lst[-1] % 2 == 0:
    del lst[:]
  
  for i in range(len(lst)):       
    if lst[i] % 2 != 0:
      del lst[:i]
      break 
      
  return lst
 
print(delete_starting_evens([4, 8, 10, 11, 12, 15]))
#[11, 12, 15]
print(delete_starting_evens([4, 8, 10]))
#[]

it works well , but i was thinking is this a good solution or a bad one ?

Another workaround using for:

def delete_starting_evens(lst):
  for i in range(len(lst)):
    if lst[0]%2 == 0:
      del lst[0]
    else:
      break
  return lst
3 Likes

That is a really nice way to present a single return. The topic came up in another thread today, along similar lines. Serendipity that you would post an extraordinary example.

Why do I get 'None'?

2 Likes

Starter with while, thought it was too long, checked this thread, thought for could work, now I see while can be used in a really nice way. Awesome!

Why doesn’t this work? I’m iterating through a copy of the list so removing elements should be fine? but I get value error.

def delete_starting_evens(lst):
  for i in lst[:]:
    while i % 2 == 0:  
      lst.remove(i)
  return lst

Got it…

I never broke out of the loop with “break” so it tried to iterate over again and remove the first number which was not there.

This works:


def delete_starting_evens(lst):
  for i in lst[:]:
    while i % 2 == 0:  
      lst.remove(i)
      break
  return lst

It doesn’t solve the problem, since I remove all even numbers but I figured out why I had the value error.

You will find that neither the .remove() method nor the nested loop are necessary. Both add behaviors that may not be expected.

Think what happens if we keep iterating until we reach the first odd number and then capture the list from that point and return it?

You’re right its not the best method, I think I got it working though.

def delete_starting_evens(lst):
  for i in lst[:]:
    if i % 2 !=0:
      return lst
    while i % 2 == 0:  
      lst.remove(i)
      break
  return lst

Now test it with,

a) an empty list; and,
b) all evens in the list.

I get an empty list returned for both cases. Is that wrong? I could add some clauses to return something else, but the question did not mention this.

That’s the correct result.

Your while-loop will never make more than one iteration, how is that different from an if-statement?

You’re using two conditions, but one is the inversion of the other. You only need to test that condition once.

Removing at the front involves copying all the following elements in order to move them 1 step closer to the beginning. Doing this repeatedly could be argued to be the wrong thing. It would be better to only write the values to keep to a new list, or to make the removals in a single operation after having identified what location the removal stops at.

def delete_starting_evens(lst):
    # find location of first non-even
    for i, x in enumerate(lst):
        if x % 2 == 1:
            # found the location of interest, remove up to here and exit
    # no such location found, remove it all.
1 Like

Yeah an if statement can replace my while loop.

The new version you have confuses me a bit. How would it work in a list of even and odd numbers? What would make the iteration stop at the first odd? Also why test for Mod 1 and not 0?

i.e [2, 4, 6, 7, 8, 10, 3]

Two of my comments say what the code that is missing there needs to do, they explain what would make it stop.

a remainder of 1 what I’m looking for, isn’t it.

1 Like

Ahhh I get it. I just had to think about it again.

Thanks!

Even better yet would be to find the location, and then delete up until there.

The difference? No nested logic. Both deletion cases also become the same one so long as one picks an appropriate value for “not found”

I ended up with this:

def delete_starting_evens(lst):
    for i, x in enumerate(lst):
        if x % 2 == 1:
          return lst[i::]

Your return value is now conditional. What if that condition is not met?

Here’s a sequential version:

def delete_starting_evens(lst):
    # find
    for loc, x in enumerate(lst):
        if x % 2 == 1:
            break
    else:
        loc = len(lst)

    # delete
    return lst[loc:]

(the else there runs if the loop did not break)

one might also want to generalize it so that the condition is a function that the caller can provide:

dropwhile(even, numbers)

and, one might imagine some companion funcitons to this: takewhile, dropendwhile, takeendwhile

hi!
i think for works well here:

def delete_starting_evens(lst):
for num in range(len(lst)):
if lst[num] % 2 == 0 and lst[num] == lst[0]:
continue
return lst[num+2:]

And the result is as expected

thanks for confirming or not…

Hello, @method9810918375.

Welcome to the forums.

When I run your function on this list: [2, 4, 6, 8, 10, 12], it should return an empty list.
It returns: [8, 10, 12]
Also, [1, 2, 3, 4, 5, 6] returns [3, 4, 5, 6] even though there were no ‘starting evens.’

you are right! frankly im trying to get the intuition behind while and for (when to use “while” or “for”)the lessons haven’t gone deeper on it. i think that if we (or should i say I) get the intuition the rest would be easy. thanks for your eventual response

1 Like