[Tutor] How do I (idiomatically) determine when I'm looking at the last entry in a list?

Steven D'Aprano steve at pearwood.info
Wed Oct 28 19:41:56 EDT 2015


On Wed, Oct 28, 2015 at 02:48:05PM +0000, Flynn, Stephen (L & P - IT) wrote:

> 	I'm iterating through a list and I'd like to know when I'm at
> the end of the said list, so I can do something different. For example
> 
> list_of_things = ['some', 'special', 'things']
> for each_entry in list_of_things:
> 	print(each_entry)
> 	if each_entry == list_of_things[-1]: # do something special to
> last entry
> 	...etc
> 
> 
> Is this the idiomatic way to detect you're at the last entry in a list
> as you iterate through it?

But it doesn't detect the last entry. Consider:

list_of_things = ["cheese", "fruit", "cheese", "fish", "cheese"]

Your code will perform the special processing three times.

There's no idiomatic way to do this because it is a fairly unusual thing 
to do. Normally we want to process all the items in a list the same way. 
But here are some solutions:

(1) Avoid the problem altogether by arranging matters so that the "last 
item" isn't in the list at all.

list_of_things = ['some', 'special']
last = 'things'
for each_entry in list_of_things:
    print(each_entry)

print(last.upper())


(2) Slicing. Requires a little extra memory, but is probably the closest 
to an idiomatic solution for this sort of thing.

list_of_things = ['some', 'special', 'things']
for each_entry in list_of_things[:-1]:
    print(each_entry)

print(list_of_things[-1].upper())


(3) Add a sentinel to the list.

list_of_things = ['some', 'special', None, 'things']
for each_entry in list_of_things:
    if each_entry is None: break
    print(each_entry)

print(list_of_things[-1].upper())


(4) Count the items.

list_of_things = ['some', 'special', 'things']
for i, each_entry in enumerate(list_of_things):
    if i == len(list_of_things) - 1:  # Watch out for off-by-one errors!
        each_entry = each_entry.upper()
    print(each_entry)


(5) What if you're dealing with an iterable of unknown length that can't 
be sliced? The obvious answer is to convert to a list:

list_of_things = list(things)

but suppose you have some reason for not wanting to do that. (Perhaps 
things is truly huge, billions of items.) Something like this should 
help:

prev = []
for this in things:
    if prev:
        print(prev[0])
    prev = [this]

print(prev[0].upper())


We can make this a little more efficient if things is an actual 
iterator:

things = iter(things)
try:
    prev = next(things)
except StopIteration:
    # No things at all.
    pass
else:
    for this in things:
        print(prev)
        prev = this
    print(prev.upper())



-- 
Steve


More information about the Tutor mailing list