Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Removal of element from list while traversing causes the next element to be skipped

10 views
Skip to first unread message

William McBrine

unread,
Jan 29, 2008, 11:34:17 AM1/29/08
to
Look at this -- from Python 2.5.1:

>>> a = [1, 2, 3, 4, 5]
>>> for x in a:
... if x == 3:
... a.remove(x)
... print x
...
1
2
3
5
>>> a
[1, 2, 4, 5]
>>>

Sure, the resulting list is correct. But 4 is never printed during the
loop!

What I was really trying to do was this:

apps = [name for name in os.listdir(ROOT) if
os.path.isdir(os.path.join(ROOT, name))]

apptitles = {}

for name in apps:
try:
app = __import__(name)
except:
apps.remove(name)
else:
apptitles[name] = getattr(app, 'TITLE', name.title())

which worked fine, until I actually had a directory with no module in it.
Then that directory was correctly removed from the list, but the _next_
one was skipped, so its title was never assigned, which caused problems
later in the program.

--
09 F9 11 02 9D 74 E3 5B D8 41 56 C5 63 56 88 C0 -- pass it on

Berteun Damman

unread,
Jan 29, 2008, 12:03:33 PM1/29/08
to
On Tue, 29 Jan 2008 16:34:17 GMT, William McBrine <wmcb...@users.sf.net> wrote:
> Look at this -- from Python 2.5.1:
>
>>>> a = [1, 2, 3, 4, 5]
>>>> for x in a:
> ... if x == 3:
> ... a.remove(x)
> ... print x
> ...
> 1
> 2
> 3
> 5
>>>> a
> [1, 2, 4, 5]

You have to iterate over a copy of 'a', so for x in a[:]. Modifying a
list while iterating over is a recipe for problems. (As it is in many
other programming languages.)

Berteun

imageguy

unread,
Jan 29, 2008, 12:06:26 PM1/29/08
to

You should not change/modify the original sequence iterating over.
In the list comprehension, try changing os.listdir(ROOT) to
os.listdir(ROOT)[:] so you are get a copy of the original list.

geoff.

attn.st...@gmail.com

unread,
Jan 29, 2008, 12:23:16 PM1/29/08
to
On Jan 29, 8:34 am, William McBrine <wmcbr...@users.sf.net> wrote:
> Look at this -- from Python 2.5.1:
>
> >>> a = [1, 2, 3, 4, 5]
> >>> for x in a:
>
> ... if x == 3:
> ... a.remove(x)
> ... print x
> ...
> 1
> 2
> 3
> 5
>
> >>> a
> [1, 2, 4, 5]
>
> Sure, the resulting list is correct. But 4 is never printed during the
> loop!
>
(snipped)


If you're going to delete elements from
a list while iterating over it, then do
it in reverse order:

>>> a = [ 98, 99, 100 ]
>>> last_idx = len(a) - 1
>>> for i, x in enumerate(a[::-1]):
... if x == 99: del(a[last_idx - i])
... print x
...
100
99
98
>>> a
[98, 100]

--
Hope this helps,
Steven

Berteun Damman

unread,
Jan 29, 2008, 12:40:10 PM1/29/08
to
On Tue, 29 Jan 2008 09:23:16 -0800 (PST), attn.st...@gmail.com
<attn.st...@gmail.com> wrote:
> If you're going to delete elements from
> a list while iterating over it, then do
> it in reverse order:

Why so hard? Reversing it that way creates a copy, so you might as
well do:


>>> a = [ 98, 99, 100 ]

>>> for i, x in enumerate(a[:]):
... if x == 99: del(a[i])
... print x

Berteun

Santiago Romero

unread,
Jan 29, 2008, 12:51:19 PM1/29/08
to
> Look at this -- from Python 2.5.1:
>
> >>> a = [1, 2, 3, 4, 5]
> >>> for x in a:
> ... if x == 3:
> ... a.remove(x)
> ... print x

Well ... you could use:

>>> for i in range(len(a)-1, -1, -1):
... print a[i]
... if a[i] == 3: del a[i]
...
5
4
3
2
1
>>> print a
[1, 2, 4, 5]


Bye.

Duncan Booth

unread,
Jan 29, 2008, 3:17:42 PM1/29/08
to
Berteun Damman <berteun@NO_SPAMdds.nl> wrote:

Why so hard?

>>> a = [ 98, 99, 100, 98, 99, 100 ]


>>> for i, x in enumerate(a[:]):

if x == 99: del(a[i])


>>> a
[98, 100, 98, 99]

oops! But:

>>> a = [ 98, 99, 100, 98, 99, 100 ]


>>> last_idx = len(a) - 1

>>> for i, x in enumerate(a[::-1]):
if x == 99: del(a[last_idx - i])


>>> a
[98, 100, 98, 100]

Reversing it works. Your code doesn't.

Paul Hankin

unread,
Jan 29, 2008, 5:49:09 PM1/29/08
to

How about...


for name in apps:
try:

app == __import__(name)


apptitles[name] = getattr(app, 'TITLE', name.title())

except ImportError:
pass

# Remove apps with no title, ie those that didn't import.
apps = [name for name in apps if apptitles.get(name)]

Alternatives for the last line would be 'apps = filter(apptitles.get,
apps)'
or 'apps = apptitles.keys()'.

--
Paul Hankin

Paul Hankin

unread,
Jan 29, 2008, 5:59:09 PM1/29/08
to
On Jan 29, 8:17 pm, Duncan Booth <duncan.bo...@invalid.invalid> wrote:
> Berteun Damman <berteun@NO_SPAMdds.nl> wrote:
> > On Tue, 29 Jan 2008 09:23:16 -0800 (PST), attn.steven....@gmail.com
> ><attn.steven....@gmail.com> wrote:
> >> If you're going to delete elements from
> >> a list while iterating over it, then do
> >> it in reverse order:
>
> > Why so hard? Reversing it that way creates a copy, so you might as
> > well do:
> >>>> a = [ 98, 99, 100 ]
> >>>> for i, x in enumerate(a[:]):
> >  ...     if x == 99: del(a[i])
> >  ...     print x
>
> Why so hard?
>
> >>> a = [ 98, 99, 100, 98, 99, 100 ]
> >>> for i, x in enumerate(a[:]):
>
>         if x == 99: del(a[i])

Why so hard? :)

a = [x for x in a if x != 99]

OK, so this doesn't modify a in place.. but how often do you really
need to do that?

If I really had to modify it in place (and the condition wasn't really
x == 99), how about:
bad_indices = [i for i, x in enumerate(a) if x == 99]
for bad_index in reversed(bad_indices):
del a[bad_index]

--
Paul Hankin

Joe Riopel

unread,
Jan 29, 2008, 9:12:46 PM1/29/08
to attn.st...@gmail.com, pytho...@python.org
On Jan 29, 2008 9:23 AM, attn.st...@gmail.com

<attn.st...@gmail.com> wrote:
> If you're going to delete elements from
> a list while iterating over it, then do
> it in reverse order:

how about
>>> li = [1,2,3,4,5]
>>> filter(lambda x: x != 3, li)
[1, 2, 4, 5]
>>>

Santiago Romero

unread,
Jan 30, 2008, 2:01:18 AM1/30/08
to
> how about
>
> >>> li = [1,2,3,4,5]
> >>> filter(lambda x: x != 3, li)
> [1, 2, 4, 5]

I haven't measured it, but this should be the fast solution in all
the thread ...

Paul Rubin

unread,
Jan 30, 2008, 2:09:56 AM1/30/08
to

li.remove(3) is probably faster.

Santiago Romero

unread,
Jan 30, 2008, 3:50:26 AM1/30/08
to
On 30 ene, 08:09, Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

But that only removes the first ocurrence of item==3.

In a = [1, 2, 3, 3, 3, 4, 3, 3, 2, 3], the filter solution will
efectively remove all items with value == 3 while li.remove(3) will
only remove the first ocurrence.

Bye!

cokof...@gmail.com

unread,
Jan 30, 2008, 4:06:38 AM1/30/08
to

from itertools import ifilter
print [x for x in ifilter(lambda x: x != 99, li)]

Will this one be faster or slower than filter?

Paul Rubin

unread,
Jan 30, 2008, 4:36:27 AM1/30/08
to
Santiago Romero <sro...@gmail.com> writes:
> In a = [1, 2, 3, 3, 3, 4, 3, 3, 2, 3], the filter solution will
> efectively remove all items with value == 3 while li.remove(3) will
> only remove the first ocurrence.

Hmm, interesting, I didn't realize that (shoulda checked the docs).
Thanks!

Arnaud Delobelle

unread,
Jan 30, 2008, 6:57:50 AM1/30/08
to
On Jan 29, 10:59 pm, Paul Hankin <paul.han...@gmail.com> wrote:
> If I really had to modify it in place (and the condition wasn't really
> x == 99), how about:
> bad_indices = [i for i, x in enumerate(a) if x == 99]
> for bad_index in reversed(bad_indices):
>     del a[bad_index]

Or one could use the trick of counting from the right (untested):

n = len(a)


for i, x in enumerate(a):

if x == 99: del a[i-n]

--
Arnaud

Neil Cerutti

unread,
Jan 30, 2008, 8:04:18 AM1/30/08
to pytho...@python.org
On Jan 30, 2008 6:57 AM, Arnaud Delobelle <arn...@googlemail.com> wrote:
> Or one could use the trick of counting from the right (untested):
>
> n = len(a)
> for i, x in enumerate(a):
> if x == 99: del a[i-n]

Or one can put on his bellbottoms, horn-rimmed glasses, and wear a mullet:

i = 0
while i < len(a):
if a[i] == 99:
del a[i]
else:
i += 1

--
Neil Cerutti <mr.cerut...@gmail.com>

bearoph...@lycos.com

unread,
Jan 30, 2008, 8:17:41 AM1/30/08
to
If you don't want to reinvent the wheel all the time you can use this
one:

def inplacefilter(pred, alist):
"""inplacefilter(pred, alist): filters the given list like
filter(),
but works inplace, minimizing the used memory. It returns None.

>>> pr = lambda x: x > 2
>>> l = []
>>> inplacefilter(pr, l)
>>> l
[]
>>> l = [1,2,2]
>>> inplacefilter(pr, l)
>>> l
[]
>>> l = [3]
>>> inplacefilter(pr, l)
>>> l
[3]
>>> l = [1,2,3,1,5,1,6,0]
>>> r = filter(pr, l) # normal filter
>>> r
[3, 5, 6]
>>> inplacefilter(pr, l)
>>> r == l
True
"""
slow = 0
for fast, item in enumerate(alist):
if pred(item):
if slow != fast:
alist[slow] = alist[fast]
slow += 1
del alist[slow:]


If you use Psyco you can replace the enumerate() with a manually
incremented counter to speed up the code a bit, like this (untested):

def inplacefilter(pred, alist):
slow = 0
fast = 0
for item in alist:
if pred(item):
if slow != fast:
alist[slow] = alist[fast]
slow += 1
fast += 1
del alist[slow:]

Bye,
bearophile

Paul Rubin

unread,
Jan 30, 2008, 8:20:49 AM1/30/08
to
"Neil Cerutti" <mr.ce...@gmail.com> writes:
> Or one can put on his bellbottoms, horn-rimmed glasses, and wear a mullet:
>
> i = 0
> while i < len(a):
> if a[i] == 99:
> del a[i]
> else:
> i += 1

Quadratic time!! Yowch!! Back to the future:

def rocket_science(xs):
for x in xs:
if x != 99:
yield x

a[:] = list(rocket_science(a))

;-)

Arnaud Delobelle

unread,
Jan 30, 2008, 8:22:45 AM1/30/08
to
On Jan 30, 11:57 am, Arnaud Delobelle <arno...@googlemail.com> wrote:

> n = len(a)
> for i, x in enumerate(a):
>     if x == 99: del a[i-n]

Oops. That can't work. Don't know what I was thinking here. I
probably did had one mental refactoring too many...

--
Arnaud

Neil Cerutti

unread,
Jan 30, 2008, 8:45:03 AM1/30/08
to pytho...@python.org
On 30 Jan 2008 05:20:49 -0800, Paul Rubin

Heh.

<lamely>It's probably a fairly peppy quadratic operation though.</lamely>

Besides, wherever will I find plutonium or a bolt of lightning?

--
Neil Cerutti <mr.cerut...@gmail.com>

Hrvoje Niksic

unread,
Jan 30, 2008, 8:52:47 AM1/30/08
to
Paul Rubin <http://phr...@NOSPAM.invalid> writes:

> Quadratic time!! Yowch!! Back to the future:
>
> def rocket_science(xs):
> for x in xs:
> if x != 99:
> yield x
>
> a[:] = list(rocket_science(a))

I call "useless use of list"!

a[:] = rocket_science(a)

:-)

cokof...@gmail.com

unread,
Jan 30, 2008, 9:07:45 AM1/30/08
to
Anyone else noticed that the OP has not actually replied to any of the
suggestions...

William McBrine

unread,
Feb 1, 2008, 12:22:46 PM2/1/08
to
On Wed, 30 Jan 2008 06:07:45 -0800, cokofreedom wrote:

> Anyone else noticed that the OP has not actually replied to any of the
> suggestions...

Sorry. I was just fascinated at the turns it was taking. But the first
answer was fine for me:

for name in apps[:]:
etc.

Thanks all.

0 new messages