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

3 views
Skip to first unread message

William McBrine

unread,
Jan 29, 2008, 4:34:17 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
...
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, 5: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, 5: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, 5: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, 5: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, 5: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, 8: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, 10: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, 10: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 30, 2008, 2:12:46 AM1/30/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, 7: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, 7:09:56 AM1/30/08
to

li.remove(3) is probably faster.

Santiago Romero

unread,
Jan 30, 2008, 8: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, 9: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, 9: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, 11: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, 1:04:18 PM1/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, 1:17:41 PM1/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, 1:20:49 PM1/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, 1:22:45 PM1/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, 1:45:03 PM1/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, 1:52:47 PM1/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, 2:07:45 PM1/30/08
to
Anyone else noticed that the OP has not actually replied to any of the
suggestions...

William McBrine

unread,
Feb 1, 2008, 5: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