1

Consider:

fooList = [1, 2, 3, 4] # Ints for example only, in real application using objects

for foo in fooList:
    if fooChecker(foo):
        remove_this_foo_from_list

How is the specific foo to be removed from the list? Note that I'm using ints for example only, in the real application there is a list of arbitrary objects.

Thanks.

dotancohen
  • 30,064
  • 36
  • 138
  • 197
  • 1
    It's worth noting the contents of [another answer](http://stackoverflow.com/a/2629240/172176), which would suggest that building a new list is more efficient than modifying one in-place. If you need to do it in-place `my_list.remove(my_item)` is particularly bad, because it's an O(n) lookup - use `del my_list[index]` instead. – Aya Jun 13 '13 at 16:37
  • @Aya but then you have to iterate by index (or use `enumerate()` - still ugly) - hence my suggestion of just building a new list then assigning back if you need to modify the existing list. Generally, of course, one can just build a new list and forget the old one. – Gareth Latty Jun 13 '13 at 16:49
  • @Lattyware Indeed. See also my comment on your answer. – Aya Jun 13 '13 at 17:06

3 Answers3

8

Iterate over a shallow copy of the list.

As you can't modify a list while iterating over so you need to iterate over a shallow copy of the list.

fooList = [1, 2, 3, 4] 

for foo in fooList[:]: #equivalent to list(fooList), but much faster
    if fooChecker(foo):
        fooList.remove(foo)
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • 1
    +1, this is the other way to do it. Just to explain, the copy is needed in order to ensure the list doesn't change while you are iterating over it, as that will cause unwanted behaviour. – Gareth Latty Jun 13 '13 at 16:22
8

Generally, you just don't want to do this. Instead, construct a new list instead. Most of the time, this is done with a list comprehension:

fooListFiltered = [foo for foo in fooList if not fooChecker(foo)]

Alternatively, a generator expression (my video linked above covers generator expressions as well as list comprehensions) or filter() (note that in 2.x, filter() is not lazy - use a generator expression or itertools.ifilter() instead) might be more appropriate (for example, a large file that is too big to be read into memory wouldn't work this way, but would with a generator expression).

If you need to actually modify the list (rare, but can be the case on occasion), then you can assign back:

fooList[:] = fooListFiltered
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • 1
    Since the OP's `fooChecker()` returns `True` to remove an item from a list, you could also mention `itertools.ifilterfalse(fooChecker, fooList)`. – Aya Jun 13 '13 at 17:05
-1

Use filter:

newList = list(filter(fooChecker, fooList))

or

newItems = filter(fooChecker, fooList))

for item in newItems:
    print item # or print(item) for python 3.x

http://docs.python.org/2/library/functions.html#filter

Paul
  • 20,883
  • 7
  • 57
  • 74
  • -1 - This will produce the list of all values that should be removed - the exact *opposite* of what was requested. – Gareth Latty Jun 13 '13 at 16:23
  • So tell me, how hard is it to negate a condition? Given the fact that `filter` can produce really readable code? – Paul Jun 13 '13 at 16:27
  • Indeed I recognize my mistake, but I think it's not fundamental and the OP can figure it out by himself. – Paul Jun 13 '13 at 16:30
  • 1
    Then change you answer to include that note - given it reverses the operation performed, it's pretty important. – Gareth Latty Jun 13 '13 at 16:30