Discussion:
[Tutor] How do I (idiomatically) determine when I'm looking at the last entry in a list?
Flynn, Stephen (L & P - IT)
2015-10-28 14:48:05 UTC
Permalink
Afternoon,

Python 3.

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?



For context, I'm working my way through a (csv) file which describes
some database tables. I'm building the Oracle DDL to create that table
as I go. When I find myself building the last column, I want to finish
the definition with a ");" rather than the usual "," which occurs at the
end of all other column definitions...

e.g.
CREATE TABLE wibble
(
Col1 CHAR(2),
Col2 NUMBER(5,2),
);

Regards,

Steve.


This email is security checked and subject to the disclaimer on web-page: http://www.capita.co.uk/email-disclaimer.aspx
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Peter Otten
2015-10-28 16:27:46 UTC
Permalink
Post by Flynn, Stephen (L & P - IT)
Afternoon,
Python 3.
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']
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?
If the list is small you can slice it:

assert items
for item in things[:-1]:
print(item, ",", sep="")
print(items[-1])

I have written a generator similar to
... items = iter(items)
... try:
... prev = next(items)
... except StopIteration:
... return
... for item in items:
... yield False, prev
... prev = item
... yield True, prev
...
... print(item, "(done)" if islast else "(to be continued)", sep="")
...
a(to be continued)
b(to be continued)
c(done)

but only used it once ore twice.
Post by Flynn, Stephen (L & P - IT)
For context, I'm working my way through a (csv) file which describes
some database tables. I'm building the Oracle DDL to create that table
as I go. When I find myself building the last column, I want to finish
the definition with a ");" rather than the usual "," which occurs at the
end of all other column definitions...
e.g.
CREATE TABLE wibble
(
Col1 CHAR(2),
Col2 NUMBER(5,2),
);
print(",\n".join(["foo", "bar", "baz"]))
foo,
bar,
baz



_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Cameron Simpson
2015-10-28 22:22:50 UTC
Permalink
Post by Flynn, Stephen (L & P - IT)
Python 3.
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']
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?
If it really is a list then enumerate is your friend.

list_of_things = ['some', 'special', 'things']
last_index = len(list_of_things) - 1
for index, each_entry in enumerate(list_of_things):
print(each_entry)
if index == last_index:
... special stuff for the last index ...
Post by Flynn, Stephen (L & P - IT)
For context, I'm working my way through a (csv) file which describes
some database tables. I'm building the Oracle DDL to create that table
as I go. When I find myself building the last column, I want to finish
the definition with a ");" rather than the usual "," which occurs at the
end of all other column definitions...
This is a bit different, in that you are probably not using a list: you don't
know how long the sequence is.

I build things like that this way:

fp.write('CREATE TABLE wibble\n(')
sep = '\n '
for item in items:
fp.write(sep)
fp.write(... column definition for item ...)
sep = ',\n '
fp.write('\n);\n')

i.e. instead of printing the separator _after_ each item, print it _before_.
That way you can special case the first occasion and use a comma for each
successive occasion.

Cheers,
Cameron Simpson <***@zip.com.au>

Why is it whatever we don't understand is called a 'thing'? - "Bones" McCoy
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-10-28 23:41:56 UTC
Permalink
Post by Flynn, Stephen (L & P - IT)
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']
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
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-10-28 23:55:03 UTC
Permalink
Wait, I have another comment...
Post by Flynn, Stephen (L & P - IT)
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
[...]
Post by Flynn, Stephen (L & P - IT)
For context, I'm working my way through a (csv) file which describes
some database tables. I'm building the Oracle DDL to create that table
as I go. When I find myself building the last column, I want to finish
the definition with a ");" rather than the usual "," which occurs at the
end of all other column definitions...
Then you don't need to know when you're at the end of the list. See
below.

But firstly, and MOST IMPORTANTLY, how are you sanitizing your input?

http://bobby-tables.com/

Code injection attacks are now #1 risk factor for code, above buffer
overflows.

So, assuming you have sanitized your input, or trust it more than you
trust your own code (you run tests on your own code, right?), let's see
how to build up a string like:

"FUNCTION(a,b,c,d,e);"

with some variable number of string arguments a, b, c, ...


args = ["5", "2", "7", "1", "0", "3"]
result = "FUNCTION(%s);" % ','.join(args)


This will do the right thing whether there is one argument or twenty, or
even no arguments at all.

The "join" method inserts the first string *between* each of the list
items. It deals correctly with the "no items" and "one item" cases:

py> "--".join([])
''
py> "--".join(["spam"])
'spam'
py> "--".join(["spam", "eggs"])
'spam--eggs'
py> "--".join(["spam", "eggs", "cheese"])
'spam--eggs--cheese'


so you don't have to worry about counting items or determining the last
item, you just join them.
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Loading...