0

i was trying to remove minimum value from a list when I saw that loop is able to iterate but it is not removing every occurance of minimum value. In this case it is -51.

marks = [-50, -50, -50 ,51]
to_pop  = -50
for i in marks:
    if i == to_pop:
        marks.remove(to_pop)

    print(f"printing list {marks} after removing {to_pop}")
~                                                              

I tried to find what is wrong with my code, using printf but could not find why list always remains as [-50, 51]. i can solve is using while loop

while to_pop in marks:
    marks.remove(to_pop)

but i want to understand the problem

1 Answers1

1

The problem is that you're trying to modify a list (by removing elements) while iterating over it. This generally doesn't behave as you'd expect in Python.

When you remove an item from a list, the list gets re-indexed. The loop doesn't know this and will continue iterating over the original indices.

Using your example list: [-50, -50, -50, 51]

  1. On the first loop iteration, i is the first -50. The loop's "internal counter" is now at 0 (the index of the first item). You then remove the first -50. The list is now [-50, -50, 51].

  2. On the second iteration, the loop's "internal counter" goes to 1 (it thinks it's at the second item), but due to the re-indexing when we removed the first item, it's looking at the third item in the original list (which is now the second item in the current list). It skips the second -50 entirely.

  3. On the third iteration, the loop's "internal counter" goes to 2, but the list is now only three items long, so the loop ends.

This is why you see that not all the -50s are removed. Using a while loop is a good solution to this issue. It checks if -50 is in the list, removes it, and then checks again from the beginning of the list, so it doesn't have the same problem.

If you want to stick with a for loop, you could create a new list that only contains the items you want to keep:

marks = [-50, -50, -50, 51]
to_pop = -50
marks = [i for i in marks if i != to_pop]
print(marks)  # [51]

This list comprehension does essentially the same thing your while loop does but in a more Pythonic and efficient way since it doesn't check every element of the list each time (the while loop condition to_pop in marks uses a for-loop in c internally).

Sash Sinha
  • 18,743
  • 3
  • 23
  • 40
  • Comprehension is the clearest approach. Out of interest to the OP, another way to avoid the remove-whilst-iterating problem is to loop **backwards** through the List, removing as you go as the index position is then not compromised by the removal. – user19077881 Aug 06 '23 at 11:35