Python: Removing list element while iterating over list [duplicate]

I'm iterating over a list of elements in Python, do some action on it, and then remove them if they meet certain criteria.

for element in somelist:
    do_action(element)
    if check(element):
        remove_element_from_list

What should I use in place of remove_element? I have seen similar questions asked, but notice the presence of the do_action part that is to be executed for all elements and thus eliminates the solution of using filters.


Solution 1:

You could always iterate over a copy of the list, leaving you free to modify the original:

for item in list(somelist):
  ...
  somelist.remove(item)

Solution 2:

To meet these criteria: modify original list in situ, no list copies, only one pass, works, a traditional solution is to iterate backwards:

for i in xrange(len(somelist) - 1, -1, -1):
    element = somelist[i]
    do_action(element)
    if check(element):
        del somelist[i]

Bonus: Doesn't do len(somelist) on each iteration. Works on any version of Python (at least as far back as 1.5.2) ... s/xrange/range/ for 3.X.

Update: If you want to iterate forwards, it's possible, just trickier and uglier:

i = 0
n = len(somelist)
while i < n:
    element = somelist[i]
    do_action(element)
    if check(element):
        del somelist[i]
        n = n - 1
    else:
        i = i + 1

Solution 3:

List comp:

results = [x for x in (do_action(element) for element in somelist) if check(element)]

Solution 4:

for element in somelist:
    do_action(element)
somelist[:] = (x for x in somelist if not check(x))

If you really need to do it in one pass without copying the list

i=0
while i < len(somelist):
    element = somelist[i] 
    do_action(element)
    if check(element):
        del somelist[i]
    else:
        i+=1

Solution 5:

You can still use filter, moving to an outside function the element modification (iterating just once)

def do_the_magic(x):
    do_action(x)
    return check(x)

# you can get a different filtered list
filter(do_the_magic,yourList)

# or have it modified in place (as suggested by Steven Rumbalski, see comment)
yourList[:] = itertools.ifilter(do_the_magic, yourList)