Find the last iteration

It's sometimes useful to determine where's the last iteration on an iterable object.

Doing this on a sized object is pretty easy. You just need to get the len() of the container object you're looking over, learn enumerate and just:

obj = (1, 2, 3)
obj_len = len(obj)
for i, item in enumerate(obj):
    do_stuff_with(item)

    # Special for last item
    if i == obj_len - 1:
        do_play_with(item)

That's OK in situations where you can count the iterations before entering in a loop, but what if the obj your code is looping over is not Sized?

This generator comes to the rescue and will notify the last iteration over any iterable object.

import collections

def notify_last_item(iterable):
    """Iterates over an iterable of items and yields a 2 tuple contaning
    (is_last, item).  `is_last` being True when last item is yielded

    :param iterable: iterable or iterator
    :yield: (is_last, item) tuple

    >>> seq = range(4)
    >>> list(notify_last_item(seq))
    [(False, 0), (False, 1), (False, 2), (True, 3)]
    """
    # Belt + braces
    if not isinstance(iterable, collections.Iterator):
        iterable = iter(iterable)

    # Pull 1st value
    last = next(iterable)  # Pull 1st value

    # Run iterator to exhaustion
    for value in iterable:
        yield False, last
        last = value

    # Report the last value
    yield True, last

Now for a simple demo:

obj = iter(range(5))  # Not sizeable
for is_last_item, item in notify_last_item(obj):
    print(is_last_item, item)

# Outputs:
# False 0
# False 1
# False 2
# False 3
# True 4

Any comment for improvements or others are welcome...

Comments !

links

social