0

This is my Python program:

def highest_even(li):
  for num in li:
    if num % 2 != 0:
      li.remove(num)
  return li

print(highest_even([10,2,3,4,5,80,15,99]))

And the output is :

[10, 2, 4, 80, 99]

I want to know why 99 wasn't deleted.

Thanks.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
lol cupid
  • 3
  • 1

8 Answers8

1

do not modify a list you iterate in

you can copy before iterate on

def highest_even(li):
  for num in li.copy():
    if num % 2 != 0:
      li.remove(num)
  return li

print(highest_even([10,2,3,4,5,80,15,99]))

Execution :

[10, 2, 4, 80]
bruno
  • 32,421
  • 7
  • 25
  • 37
1

It's generally a bad idea to modify a list that you're iterating over, all sorts of strange things can happen. The particular strange thing that's happening here has to do with the way Python iterates over lists.

Think of Python maintaining the position of the current item for iterating. When you delete an item, the others all shift "left" in the list but Python still advances the position. That means, if you have two odd numbers in a row (like 15 and 99)(a), deleting the 15 moves 99 to the left (to where the 15 was) but the next iteration will be looking at where the 99 was before shifting, not where it is now.

For example, consider the list [1, 3, 6, 8], these are the steps Python will take:

List          Pos  newList    newPos  comment
----          ---  -------    ------  -------
[1, 3, 6, 8]   0   [3, 6, 8]     1    Odd 1, delete, advance.
[3, 6, 8]      1   [3, 6, 8]     2    Even 6, leave, advance.
[3, 6, 8]      2   [3, 6, 8]     3    Even 8, leave, advance.
[3, 6, 8]      3                      iteration finished.

You can see that consecutive odd numbers cause a problem here, it will only delete alternate ones.

As to the solution: if, as your code suggests, you just want the even numbers in the list, you can use the much more succinct (and Pythonic) list comprehension (no need even for a function to do this):

myList = [10, 2, 3, 4, 5, 80, 15, 99]
evens = [item for item in myList if item % 2 == 0]
# gives [10, 2, 4, 80]

And, for completeness, since your function name seems to indicate you want the highest even number, that would be something like:

biggestEven = max([item for item in myList if item % 2 == 0])
# gives 80

(a) Your problem actually has nothing to do with the fact the 99 is at the end of the list, any consecutive odd numbers, anywhere in the list, would cause the same issue.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
0

As so many comments mention: it's unsafe (or at least it presents some unexpected behavior) to modify a list as you iterate over it. The usual fix here for lists that aren't giant is simply to copy the list when you go to iterate on it.

for num in li[:]:
   # everything as before

That little slice syntax makes Python take the list li, create a new list of its entire contents, and iterate over that, instead. Now since you're removing things from li, but iterating over the copy of li made by slicing it, there's no issue.

Adam Smith
  • 52,157
  • 12
  • 73
  • 112
0

this is because of you iterated through the list while editing it.

only_even = []
for n in lst:
    if not n % 2:
       only_even.append(n)

other methods

only_even = [n for n in lst if not n % 2]
only_even = list(filter(lambda x: not n % 2, lst))
Hyperx837
  • 773
  • 5
  • 13
0

What's happening there is that you are changing a list while you iterate over it, but the iterator on that list will not get updated with your changes. you can try this:

for i in range len(li):
j3py
  • 1,119
  • 10
  • 22
0

You should never update the data structure you are iterating in, to maintain the invariants of the loops.

If you print the li, and num right after the for num in li:, you'll see that after you remove the element from the list, the next element is skipped, meaning that the indexed is moved forward, same thing happens to 99 element.

You can check it here.

def highest_even(li):
  for num in li:
    print(li, num)
    if num % 2 != 0:
      li.remove(num)
return li

gives the output:

In [3]: highest_even([10,2,3,4,5,80,15,99])
([10, 2, 3, 4, 5, 80, 15, 99], 10)
([10, 2, 3, 4, 5, 80, 15, 99], 2)
([10, 2, 3, 4, 5, 80, 15, 99], 3)
([10, 2, 4, 5, 80, 15, 99], 5)
([10, 2, 4, 80, 15, 99], 15)
Out[3]: [10, 2, 4, 80, 99]
thelogicalkoan
  • 620
  • 1
  • 5
  • 13
0

You should not iterate through a list and delete the elements of the same list. Since the index is used iterate in a loop. 0- 10 1- 2 2- 3 3- 4 4- 5 5- 80 6- 15 7- 99 And as when you delete the elements from list it skips the next element. In your example, for the indexes 0 & 1 nothing changes. But when index =3 and as per the condition this element is removed and the list would be updated to [10,2,4,5,80,15,99].

After index =3 the next index is 4 and li[4] equals 5 and not 4. And your condition is not even checked for the element 4. It just happened to be even to be correct. Having some odd instead of 4 would again give you wrong output. Same is the case with the last element 99. Since the previous element 15 or index = 6 is removed the length of the list reduced by 1 and it does not loop for index = 4 as index reached its max value of updated list is 5(after removing 3,5 & 15).

0

You shouldn't update a list while iterating over it. But you can make it work by going backwards or you will be cutting a tree branch while sitting on it.

li = [10,2,3,4,5,80,15,99]

for i in range(len(li) - 1, -1, -1):
    if (i%2 != 0 ):
        del li[i]
print(li)
bhatnaushad
  • 372
  • 1
  • 2
  • 16