Coder Perfect

How do you iterate while removing things from a list?

Problem

In Python, I’m iterating over a list of tuples, seeking to delete those that fulfill particular criteria.

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

In place of code to remove tup, what should I use? I’m stumped as to how to remove the item in this manner.

Asked by lfaraone

Solution #1

You can use a list comprehension to create a new list containing only the elements you don’t want to remove:

somelist = [x for x in somelist if not determine(x)]

You may also alter the existing list to contain only the things you wish by giving somelist[:] to the slice:

somelist[:] = [x for x in somelist if not determine(x)]

If there are other references to somelist that need to be updated, this approach might be handy.

You may also use itertools instead of a comprehension. Python 2 has the following features:

from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)

Alternatively, in Python 3:

from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)

Answered by David Raznick

Solution #2

The solutions that suggest list comprehensions are almost correct, except that they create an entirely new list and then give it the same name as the previous list, rather than modifying the existing list in place. That’s not the same as selective removal, as suggested by @Lennart: it’s faster, but if your list is accessed by many references, the fact that you’re just reseating one of the references and not the list object itself might lead to subtle, devastating issues.

Fortunately, obtaining both the speed of list comprehensions AND the requisite semantics of in-place change is fairly simple — simply code:

somelist[:] = [tup for tup in somelist if determine(tup)]

This answer differs slightly from the others in that it assigns to a list slice that happens to be the full list, thereby replacing the list contents within the same Python list object, rather than simply reseating one reference (from previous list object to new list object) like the others do.

Answered by Alex Martelli

Solution #3

You must first make a copy of the list and iterate over it, else the iteration would fail with unexpected results.

For instance (depending on the list type):

for tup in somelist[:]:
    etc....

An example:

>>> somelist = range(10)
>>> for x in somelist:
...     somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]

>>> somelist = range(10)
>>> for x in somelist[:]:
...     somelist.remove(x)
>>> somelist
[]

Answered by Lennart Regebro

Solution #4

for i in range(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

If you don’t go backwards, it’ll seem like you’re sawing off the tree limb you’re sitting on:-)

Users of Python 2 should use xrange instead of range to avoid creating a hardcoded list.

Answered by John Machin

Solution #5

Overview of workarounds

Either:

If you’re doing something quick and dirty and don’t want to add a custom LinkedList class, you should go with the faster option. Unless memory is a major problem, use the add() option by default.

“for Statements” is a section of the official Python 2 tutorial 4.2.

https://docs.python.org/2/tutorial/controlflow.html#for-statements

This section of the documents clarifies that:

“The for statement” is described in Python 2 manual 7.3.

https://docs.python.org/2/reference/compound_stmts.html#for

This section of the documentation emphasizes the importance of making a copy and provides an example of how to do so:

    if x < 0: a.remove(x)

However, I disagree with this implementation because.remove() must loop through the entire list in order to locate the value.

Is there a way to have Python do this better?

This Python API appears to have room for improvement. For example, consider the following:

Both of them make it apparent that you can only alter a list being iterated via the iterator itself, and they provide efficient ways to do so without having to replicate the list.

Perhaps the underlying rationale is because Python lists are expected to be dynamic array backed, making any type of removal wasteful in any case, whereas Java’s ArrayList and LinkedList implementations of ListIterator have a nicer interface hierarchy.

In the Python standard library, there doesn’t appear to be an explicit linked list type: Linked List in Python

Answered by Ciro Santilli 新疆再教育营六四事件法轮功郝海东

Post is based on https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating