15

In Java I can do by using an Iterator and then using the .remove() method of the iterator to remove the last element returned by the iterator, like this:

import java.util.*;

public class ConcurrentMod {
    public static void main(String[] args) {
        List<String> colors = new ArrayList<String>(Arrays.asList("red", "green", "blue", "purple"));
        for (Iterator<String> it = colors.iterator(); it.hasNext(); ) {
            String color = it.next();
            System.out.println(color);
            if (color.equals("green"))
                it.remove();
        }
        System.out.println("At the end, colors = " + colors);
    }
}

/* Outputs:
red
green
blue
purple
At the end, colors = [red, blue, purple]
*/

How would I do this in Python? I can't modify the list while I iterate over it in a for loop because it causes stuff to be skipped (see here). And there doesn't seem to be an equivalent of the Iterator interface of Java.

Uli Köhler
  • 13,012
  • 16
  • 70
  • 120
user102008
  • 30,736
  • 10
  • 83
  • 104
  • I wonder if using a reverse iterator could be a solution. Any thoughts on this? It would be better than making a copy of a list. – Craig McQueen Aug 30 '09 at 02:59
  • http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python It gets asked alot ... – Jochen Ritzel Aug 30 '09 at 03:07

4 Answers4

30

Best approach in Python is to make a new list, ideally in a listcomp, setting it as the [:] of the old one, e.g.:

colors[:] = [c for c in colors if c != 'green']

NOT colors = as some answers may suggest -- that only rebinds the name and will eventually leave some references to the old "body" dangling; colors[:] = is MUCH better on all counts;-).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 2
    List comprehension is the best choice. – hughdbrown Aug 30 '09 at 03:07
  • or colors=list(c for c in colors if c != 'green') – dugres Aug 30 '09 at 09:17
  • @dugres: not quite: colors = list(...) does rebind. Alex insisted on the idea that it's better not do leave useless lists dangling in memory. – Eric O. Lebigot Aug 30 '09 at 09:39
  • well, colors[:] = ... is only better if there are other references to worry about (for some reason, this rarely turns out to be the case for me). Still, the list comprehension is definitely the way to go. – Devin Jeanpierre Aug 30 '09 at 15:21
  • 1
    @Devin, NOT just for other references. E.g. if colors is a global doing `colors=` in a fuction requires an extra `global colors`, `colors[:]=` **doesn't**. GC of the old list does't happen instantly in all versions of Python. Etc: there's NEVER any downside in assigning to `name[:]`, OFTEN many downsides to assigning to `name` (including the occasional puzzling bug where the "rarely for you" case DOES occur but you're used to the wrong way), so it's a hiding to nothing FOR the correct way, `name[:]=`, and AGAINST the wrong one, `name=`. Only one obvious way... – Alex Martelli Aug 30 '09 at 15:38
  • 3
    ...though it may not be obvious unless you're Dutch;-). – Alex Martelli Aug 30 '09 at 15:39
25

Iterate over a copy of the list:

for c in colors[:]:
    if c == 'green':
        colors.remove(c)
Arkady
  • 14,305
  • 8
  • 42
  • 46
  • Why colors[:] instead of colors? – hughdbrown Aug 30 '09 at 03:07
  • 5
    `colors[:]` is a copy (a weird but, sigh, idiomatic way to spell `list(colors)`) so it doesn't get affected by the `.remove` calls. – Alex Martelli Aug 30 '09 at 03:28
  • 1
    The only reason to call it more idiomatic is because the stdlib copy module documentation references it. Despite that, I would still use list(otherlist) for copies (or possibly copy.copy(otherthing)) – Devin Jeanpierre Aug 30 '09 at 15:18
4

You could use filter function:

>>> colors=['red', 'green', 'blue', 'purple']
>>> filter(lambda color: color != 'green', colors)
['red', 'blue', 'purple']
>>>
del-boy
  • 3,556
  • 3
  • 26
  • 39
0

or you also can do like this

>>> colors = ['red', 'green', 'blue', 'purple']
>>> if colors.__contains__('green'):
...     colors.remove('green')
user149513
  • 1,732
  • 2
  • 11
  • 11