How to detect the last element in a for loop

Alex Martelli aleax at aleax.it
Sat Jul 27 16:42:03 EDT 2002


Tom Verbeure wrote:

> 
> Hello All,
> 
> I often have the case where I need to loop through a bunch of elements,
> but do something special on for the last line of code.

That's not what you're doing below:

> Currently, I can solve it this way:
> 
>     first = 1
>     for port in self.portDefsList:
>         if first == 0:
>             string += ", "
>         else:
>             first = 0
>         string += str(port)
>         pass

Here, you're doing something special (avoiding the += ", ") the FIRST
time.  The pass is unconditional and adds absolutely nothing -- just
take it away.

Anyway, this specific need is met to perfection by:
    string += ", ".join([ str(port) for port in self.portDefsList ])

joiner.join(sequenceOfStrings) is a powerful way to meet many
problems of the "I must do something each time except the first/last"
variety -- all those problems which boil down to joining strings
with some joiner.

As a nice plus, it's also *WAY, WAY* faster.  A loop with += takes
O(N squared) time, joiner.join takes O(N) time, where N is the
size of the input.  Don't use loops of += on strings except in
really short, trivial cases -- prepare a list and join it at the
end, or use a cStringIO instance x and call x.write, etc, etc.


For more general needs, you can sometimes use slices to good effect.
I.e., instead of:

first = 1
for item in somelist:
    if first:
        processfirst(item)
        first = 0
    else:
        processnormal(item)

you can do:

processfirst(somelist[0])
for item in somelist[1:]:
    processnormal(item)

and if the special case needs to be the LAST one:

for item in somelist[:-1]:
    processnormal(item)
processlast(somelist[-1])


This does assume that you have a sequence, not just an iterator, and can 
thus slice off the first or last items easily.  It's trickier but
not difficult to do special-on-first on an iterator:

processfirst(iterator.next())
for item in iterator:
    processnormal(item)

the .next call returns and "consumes" the first item, so the following
for starts from the second one, and all is sweetness and light.


Detecting the LAST item for special processing is trickier still if
what you DO have is an iterator.  You basically have to "stagger"
the output, e.g.:

saved = iterator.next()
for item in iterator:
    processnormal(saved)
    saved = item
processlast(saved)

There's no way out here from the "extra variable" -- you may hide
it somewhere, but SOMEwhere it does have to be.


Alex




More information about the Python-list mailing list