Cleanest Way to Get Last Item from Python Iterator

Cleanest way to get last item from Python iterator

item = defaultvalue
for item in my_iter:
pass

Fastest way to get the first and last element of a python iterator

Get the first value with the next() function:

first = last = next(iterable, defaultvalue)
for last in iterable:
pass

This assumes the iterable is finite.

For an empty iterable, first and last are set to defaultvalue. For an iterable with just one element, first and last will both refer to that one element. For any other finite iterable, first will have the first element, last the very last.

Last element in a python iterator

Here is a wrapper class for an iterator that does give you a hasNext property:

class IteratorEx(object):
def __init__(self, it):
self.it = iter(it)
self.sentinel = object()
self.nextItem = next(self.it, self.sentinel)
self.hasNext = self.nextItem is not self.sentinel

def next(self):
ret, self.nextItem = self.nextItem, next(self.it, self.sentinel)
self.hasNext = self.nextItem is not self.sentinel
return ret

def __iter__(self):
while self.hasNext:
yield self.next()

Demo:

iterex = IteratorEx(xrange(10)) 
for i in iterex:
print i, iterex.hasNext

Prints:

0 True
1 True
2 True
3 True
4 True
5 True
6 True
7 True
8 True
9 False

Python get the last element from generator items

You have to iterate through the whole thing. Say you have this generator:

def foo():
yield 0
yield 1
yield 2
yield 3

The easiest way to get the first and last value would be to convert the generator into a list. Then access the values using list lookups.

data = list(foo())
print(data[0], data[-1])

If you want to avoid creating a container, you could use a for-loop to exhaust the generator.

gen = foo()
first = last = next(gen)
for last in gen: pass

print(first, last)

Note: You'll want to special case this when there are no values produced by the generator.

How to Get Last Item in a Python Generator

The last item of a generator cannot (always) be determined.

Of some generators you cannot predict if they'll ever end (or the last element is uncertain):

import random

def random_series():
while x := random.randint(1, 10) > 1:
yield x

# print random numbers from generator until 1 is generated
for x in random_series():
print(x)

Others will literally go on forever:

def natural_numbers():
n = 0
while True:
n += 1
yield n

# prints the first 10 natural numbers, but could go on forever
g = natural_numbers()
for _ in range(10):
print(next(g))

However, every generator is an iterator, and you can try to get the last item (or the number of items) the same way you can for any other iterator that doesn't flat out tell you, or allow indexing.

For iterators that do:

# if i is some iterator that allows indexing and has a length:
print('last element: ', i[-1])
print('size: ', len(i))

For iterators that don't (but at least end):

print('last element: ', list(i)[-1])
print('size: ', len(list(i)))

However, if you try that on an infinite generator, your code will hang, or more likely crash as soon as it runs out of memory to put the list into. Also, note that every time you call list(i), it will construct a new list, so if you need that list multiple times, you may want to assign the result to a variable to save time.

In your case:

items = list(obj.get_items())
print("tweets: ", len(items))
print("last tweet: ", items[-1])

Note: as user @kellybundy points out, creating a list is not very memory-efficient. If you don't care about the actual contents, other than the last element, this would work:

for n, last in enumerate(obj.get_items()):
pass
# n will be the number of items - 1 and last will be the last item

This is memory-efficient, but the contents of the generator are now lost.

What is the pythonic way to detect the last element in a 'for' loop?

Most of the times it is easier (and cheaper) to make the first iteration the special case instead of the last one:

first = True
for data in data_list:
if first:
first = False
else:
between_items()

item()

This will work for any iterable, even for those that have no len():

file = open('/path/to/file')
for line in file:
process_line(line)

# No way of telling if this is the last line!

Apart from that, I don't think there is a generally superior solution as it depends on what you are trying to do. For example, if you are building a string from a list, it's naturally better to use str.join() than using a for loop “with special case”.


Using the same principle but more compact:

for i, line in enumerate(data_list):
if i > 0:
between_items()
item()

Looks familiar, doesn't it? :)


For @ofko, and others who really need to find out if the current value of an iterable without len() is the last one, you will need to look ahead:

def lookahead(iterable):
"""Pass through all values from the given iterable, augmented by the
information if there are more values to come after the current one
(True), or if it is the last value (False).
"""
# Get an iterator and pull the first value.
it = iter(iterable)
last = next(it)
# Run the iterator to exhaustion (starting from the second value).
for val in it:
# Report the *previous* value (more to come).
yield last, True
last = val
# Report the last value.
yield last, False

Then you can use it like this:

>>> for i, has_more in lookahead(range(3)):
... print(i, has_more)
0 True
1 True
2 False

Python How to I check if last element has been reached in iterator tool chain?

When the loop ends, the elt variable doesn't go out of scope, and still holds the last value given to it by the loop. So you could just put the code at the end of the loop and operate on the elt variable. It's not terribly pretty, but Python's scoping rules aren't pretty either.

The only problem with this (thanks, cvondrick) is that the loop might never execute, which would mean that elt doesn't exist - we'd get a NameError. So the full way to do it would be roughly:

del elt # not necessary if we haven't use elt before, but just in case
for elt in itertools.chain.from_iterable(node):
do_stuff_to_each(elt)
try:
do_stuff_to_last(elt)
except NameError: # no last elt to do stuff to
pass


Related Topics



Leave a reply



Submit