How to treat the first or last item differently

Terry Reedy tjreedy at udel.edu
Tue Jul 20 17:58:33 EDT 2010


A Python newcomer asked this question on python-ideas list.
I am answering here for the benefit of others.

Example: building a string res with commas separating substrings s from 
some sequence. Either the first item added must be s versus ', '+s or 
the last must be s versus s+', '.

For building strings, of course, the join method solves the problem, 
adding n-1 separators between n items.

items = ['first', 'second', 'last']
print(', '.join(items))
#first, second, last

DISCLAIMER: All of the following code chunks produce the same result, 
for the purpose of illustration, but they are NOT the way to build a 
string result with dependable efficiency.

To treat the first item differently, either peel it off first ...

it = iter(items)
try: # protect against empty it, simplify if know not
     res = next(it)
     for s in it: res += ', ' + s
except StopIteration:
   res = ''
print(res)
# first, second, last

or use a flag.

res = ''
First = True
for s in items:
     if First:
         res=s
         First=False
     else:
         res += ', ' + s
print(res)
# first, second, last

There is no way, in general, to know whether next(it) will yield another 
item after the current one. That suggests that the way to know whether 
an item is the last or not is try to get another first, before 
processing the current item.

One approach is to look ahead ...

it = iter(items)
res = ''
try:
     cur = next(it)
     for nxt in it:
         # cur is not last
         res += cur + ', '
         cur = nxt
     else:
         # cur is last item
         res += cur
except StopIteration:
     pass
print(res)
# first, second, last

Another is to add a unique sentinel to the sequence.

Last = object()
items.append(Last) # so not empty, so no protection against that needed
it = iter(items)
res = ''
cur = next(it)
for nxt in it:
     if nxt is not Last:
         res += cur + ', '
         cur = nxt
     else:
         res += cur

print(res)
# first, second, last

It makes sense to separate last detection from item processing so last 
detection can be put in a library module and reused.

def o_last(iterable):
     " Yield item,islast pairs"
     it = iter(iterable)
     cur = next(it)
     for nxt in it:
         yield cur,False
         cur = nxt
     else:
         yield cur,True

def comma_join(strings):
     res = ''
     for s,last in o_last(strings):
         res += s
         if not last: res += ', '
     return res

print(comma_join(['first', 'second', 'last']))
print(comma_join(['first', 'last']))
print(comma_join(['last']))
print(comma_join([]))
# first, second, last
# first, last
# last
#

-- 
Terry Jan Reedy




More information about the Python-list mailing list