• remove
  • iteration
  • list

When removing items from a list while iterating over the same list, a naive solution using list.remove can cause the iterator to skip elements:

>>> lst = [1, 2, 3, 4, 5, 6, 7, 8]
>>> for x in lst:
        if x < 6:
            lst.remove(x)

>>> lst
[2, 4, 6, 7, 8]

The reason for this is that the iterator does not know that a list element was removed, and happily advances to the next item.

In the above example, on the first iteration, the iterator looks at the first element, the 1. In the loop body, the 1 is removed from the list, making 2 the first element in the list. The iterator still points to the first element (now 2). For the second iteration, the iterator now moves on to the next element (the second element, 3), even though the new first element (2) was never being looked at. So this solution skips elements.

This behavior is properly explained in this question.

The common solution for this is to use a list comprehension which just filters the source list:

>>> lst = [1, 2, 3, 4, 5, 6, 7, 8]
>>> [x for x in lst if x >= 6]
[6, 7, 8]

Another solution would be to iterate in reverse. That way, no elements can be skipped since removing an item from the list will only affect the indexes of elements that were already handled. This can either be done manually using index-based iteration starting from the end, or using reversed():

>>> lst = [1, 2, 3, 4, 5, 6, 7, 8]
>>> for x in reversed(lst):
        if x < 6:
            lst.remove(x)

>>> lst
[6, 7, 8]

Finally, one can also iterate over a copy of the list, so when removing elements from the original list, the iterator is not affected:

>>> lst = [1, 2, 3, 4, 5, 6, 7, 8]
>>> for x in list(lst):
        if x < 6:
            lst.remove(x)