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

One question I have about the ‘while’ solution is, how is it iterating when there is no explicit formulation for it?

while len(lst) > 0:
if lst[0] % 2 == 0:
lst.pop(0)
else:
break
return lst

Did you test this against an empty list? I believe it will still raise an error.

Is the list always going to be reaching zero length? If not, then that will result in an infinite loop.

A `while` loop iterates through its code block repeatedly until the break condition is met. What change in state is taking place inside the loop (besides change in length)?

``````while lst[0] % 2 == 0:
``````

That will keep the loop going until `lst[0]` is an odd number. You won’t need an `if` statement, and neither will you need a `break`. Just a `pop` statement in the loop, and a return statement after the loop.

I agree the while formulation is short and makes more sense.
Just for information, the ‘for’ loop also works as expected in all cases

Actually, I hadn’t considered that the loop doesn’t run if the list is empty. D’oh!

I’m still a little miffed as to how it doesn’t raise an error on all even numbers. At some point the list is empty and that should raise an IndexError when we try to poll `lst[0]`. I’ve confirmed that this does not happen, so cannot eplain why. It would sit better if we reversed the order of testing to,

``````if lst == [] or lst[0] % 2 != 0:
``````

but it would also be nice to know why and error is not raised in your code. I suspect it is because of the loop never seeing an empty list. If that is the case, then we don’t need to check for an empty list.

``````>>> def delete_starting_evens(lst):
for z in range(len(lst)):
print (z)
if lst[0] % 2 != 0:
break
else:
lst.remove(lst[0])
return lst

>>> delete_starting_evens([2,4,6,8,10])
0
1
2
3
4
[]
>>> delete_starting_evens([])
[]
>>>
``````

The index position 0 will never be empty, the most you items you can remove are the same as in the length of the list, after which the list will be empty. Give the code doesn’t try to access index position 0 after removing the last item, there is no reason it should throw an error.

``````>>> def delete_starting_evens(lst):
for z in range(len(lst)):
print("count: {0}".format(z))
if lst[0] % 2 != 0:
break
else:
print("List item at 0: {0}".format(lst[0]))
lst.remove(lst[0])
return lst

>>> delete_starting_evens([2,4,6,8,10])
count: 0
List item at 0: 2
count: 1
List item at 0: 4
count: 2
List item at 0: 6
count: 3
List item at 0: 8
count: 4
List item at 0: 10
[]
``````
1 Like

My Code Doesn’t Work?
So I made a code that I thought would work for this challenge. It’s quite different from the solution but …

``````def delete_starting_evens(lst):
for i in lst:
if i %2 == 0:
lst.pop(0)
return lst

print(delete_starting_evens([4, 8, 10, 11, 12, 15]))
print(delete_starting_evens([4, 8, 10]))
``````

The resulting output was [11,12,15] and [10]

I have no idea why the [10] was left over. Should the [10] have been popped out too?

When mutating the list we are iterating, all manner of things can go wrong, in particular, things are not where they are expected to be. Consider using an approach that does not alter the list being iterated.

What happens if we iterate a copy, not the actual list.

``````>>> def delete_starting_evens(lst):
for i in lst[:]:
if i % 2 == 0:
lst.pop(0)
return lst

>>> delete_starting_evens([4, 8, 10])
[]
>>>
``````

## can please help me with the answers below, why below doesnt work ?

thanks!!

why would it work? You never modify the list, `continue` means literally that: do nothing and continue with the next iteration of the loop

Hi mtf,

lst = [4, 8, 10, 11, 12, 15]
#lst = [4, 8, 10, 12, 16]
#lst=
for i in lst:
if i % 2 == 0:
lst = lst[1:]
else:
break
print(lst)

The above code gives the following results
[11,12,15]

Doesn’t this assignment lst = lst[1:] mutate the lst list in the for loop and messes up the iterator index as in the following example?

#lst = [4, 8, 10, 11, 12, 15]
lst = [4, 8, 10, 12, 16]
for i in lst:
del lst[0]
print(lst)

[ 11, 12, 15]
[12, 16]

Thanks.

What does that do, and, does that involve changing the original list in a way that would disturb iteration?
You’re not going to want to do this, but for another reason, again apparent when considering what it does. If you carried this out manually, you’d do it differently.

So you need to know what things do before you use them, and then a whole lot of questions like that are already answered.

Any removal of elements in a list will mess with the iteration, especially if the removed element is at the front. It will cause the wrong element to be next. A while loop is better suited to this sort of operation.

From the earlier messages I understand it is better to use ‘while’ loop or use of a copy of the ‘list’ in ‘for’ loop for iteration to avoid issues. But I am quite intrigued why lst = lst[1:] worked in the following code? Is it because it is a copy assignment which do not disturb the original lst in the ‘for’ loop? But both have the same list name ‘lst’

``````lst = [4, 8, 10, 11, 12, 15]
#lst = [4, 8, 10, 12, 16]
for i, e in enumerate(lst):
print(i, e)
if lst[0] % 2 == 0:
lst = lst[1:]
else:
break
print(lst)
print(lst)
``````

As you can see the results have correct index and values. None are skipped.

0 4
[8, 10, 11, 12, 15]
1 8
[10, 11, 12, 15]
2 10
[11, 12, 15]
3 11
[11, 12, 15]

Compare the above with the following which gives wrong results.

``````lst = [4, 8, 10, 11, 12, 15]
#lst = [4, 8, 10, 12, 16]
for i, e in enumerate(lst):
print(i, e)
del lst[0]
print(lst)
``````

0 4
[8, 10, 11, 12, 15]
1 10
[10, 11, 12, 15]
2 12
[11, 12, 15]

Thanks for your help.

Changing variables will have no effect on iteration when none of your variables are used for iteration

Changing values will have effect when those values are used for iteration

But how many copies do you make?

This is wrong (in the sense that you would not do this manually so neither should your program) in almost any context. Lists do not support deletion at the front

I guess you could say this for the whole statement, but that’s two separate actions, copying and assigning

The iterator is composed from the list and returns index-value pair tuples. `i` and `e` are the unpacked tuple values.

Eg.

``````(0, 4), (1, 8), (2, 10), (3 11), (4, 12), (5, 15)
``````

That’s why you can do anything you want to the actual list.

That’s exactly what the whole thread is about. Can’t.

Hello rrtocode,

I have also used “for” and slicing and in this case the code works. Didn’t really understand from the replies why in this case it does and in the others it doesn’t.

``````def delete_starting_evens(lst):
for num in lst:
if num % 2 == 0:
lst = lst[1:]
else:
break
return lst
``````

wrt the title, you’re not removing values, you’re making copies
(also I suggest making a single copy once you’ve found the location of the first odd value (or end), instead of making a copy during each iteration (you’re not using it anyway, you’re only using the last one)

1 Like

Can someone explain this?

Picture one is my trial. Picture two is the standard answer. I try to get an empty list by using my for loop. However, it seems that the still satisfy the condition of my for loop and somehow make the become a None object. Can someone please explain this?

Be sure that your return statement is not inside the loop.