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

Viewed 306225

I'd like to know the best way (more compact and "pythonic" way) to do a special treatment for the last element in a for loop. There is a piece of code that should be called only between elements, being suppressed in the last one.

Here is how I currently do it:

for i, data in enumerate(data_list):
    code_that_is_done_for_every_element
    if i != len(data_list) - 1:
        code_that_is_done_between_elements

Is there any better way?

Note: I don't want to make it with hacks such as using reduce. ;)

33 Answers

you can determine the last element with this code :

for i,element in enumerate(list):
    if (i==len(list)-1):
        print("last element is" + element)

We can achieve that using for-else

cities = [
  'Jakarta',
  'Surabaya',
  'Semarang'
]

for city in cities[:-1]:
  print(city)
else:
  print(' '.join(cities[-1].upper()))

output:

Jakarta
Surabaya
S E M A R A N G

The idea is we only using for-else loops until n-1 index, then after the for is exhausted, we access directly the last index using [-1].

Better late than never. Your original code used enumerate(), but you only used the i index to check if it's the last item in a list. Here's an simpler alternative (if you don't need enumerate()) using negative indexing:

for data in data_list:
    code_that_is_done_for_every_element
    if data != data_list[-1]:
        code_that_is_done_between_elements

if data != data_list[-1] checks if the current item in the iteration is NOT the last item in the list.

Hope this helps, even nearly 11 years later.

For me the most simple and pythonic way to handle a special case at the end of a list is:

for data in data_list[:-1]:
    handle_element(data)
handle_special_element(data_list[-1])

Of course this can also be used to treat the first element in a special way .

Instead of counting up, you can also count down:

  nrToProcess = len(list)
  for s in list:
    s.doStuff()
    nrToProcess -= 1
    if nrToProcess==0:  # this is the last one
      s.doSpecialStuff()

There can be multiple ways. slicing will be fastest. Adding one more which uses .index() method:

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]

I will provide with a more elegant and robust way as follows, using unpacking:

def mark_last(iterable):
    try:
        *init, last = iterable
    except ValueError:  # if iterable is empty
        return

    for e in init:
        yield e, True
    yield last, False

Test:

for a, b in mark_last([1, 2, 3]):
    print(a, b)

The result is:

1 True
2 True
3 False

If you are looping the List, Using enumerate function is one of the best try.

for index, element in enumerate(ListObj):
    # print(index, ListObj[index], len(ListObj) )

    if (index != len(ListObj)-1 ):
        # Do things to the element which is not the last one
    else:
        # Do things to the element which is the last one

If you are happy to be destructive with the list, then there's the following. We are going to reverse the list in order to speed up the process from O(n^2) to O(n), because pop(0) moves the list each iteration - cf. Nicholas Pipitone's comment below

data_list.reverse()
while data_list:
   value = data_list.pop()
   code_that_is_done_for_every_element(value)
   if data_list:
       code_that_is_done_between_elements(value)
   else:
       code_that_is_done_for_last_element(value)


This works well with empty lists, and lists of non-unique items. Since it's often the case that lists are transitory, this works pretty well ... at the cost of destructing the list.

One simple solution that comes to mind would be:

for i in MyList:
    # Check if 'i' is the last element in the list
    if i == MyList[-1]:
        # Do something different for the last
    else:
        # Do something for all other elements

A second equally simple solution could be achieved by using a counter:

# Count the no. of elements in the list
ListLength = len(MyList)
# Initialize a counter
count = 0

for i in MyList:
    # increment counter
    count += 1
    # Check if 'i' is the last element in the list
    # by using the counter
    if count == ListLength:
        # Do something different for the last
    else:
        # Do something for all other elements

Just check if data is not the same as the last data in data_list (data_list[-1]).

for data in data_list:
    code_that_is_done_for_every_element
    if data != data_list[- 1]:
        code_that_is_done_between_elements

So, this is definitely not the "shorter" version - and one might digress if "shortest" and "Pythonic" are actually compatible.

But if one needs this pattern often, just put the logic in to a 10-liner generator - and get any meta-data related to an element's position directly on the for call. Another advantage here is that it will work wit an arbitrary iterable, not only Sequences.

_sentinel = object()

def iter_check_last(iterable):
    iterable = iter(iterable)
    current_element = next(iterable, _sentinel)
    while current_element is not _sentinel:
        next_element = next(iterable, _sentinel)
        yield (next_element is _sentinel, current_element)
        current_element = next_element
In [107]: for is_last, el in iter_check_last(range(3)):
     ...:     print(is_last, el)
     ...: 
     ...: 
False 0
False 1
True 2

This is an old question, and there's already lots of great responses, but I felt like this was pretty Pythonic:

def rev_enumerate(lst):
    """
    Similar to enumerate(), but counts DOWN to the last element being the
    zeroth, rather than counting UP from the first element being the zeroth.

    Since the length has to be determined up-front, this is not suitable for
    open-ended iterators.

    Parameters
    ----------
    lst : Iterable
        An iterable with a length (list, tuple, dict, set).

    Yields
    ------
    tuple
        A tuple with the reverse cardinal number of the element, followed by
        the element of the iterable.
    """
    length = len(lst) - 1
    for i, element in enumerate(lst):
        yield length - i, element

Used like this:

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    if not num_remaining:
        print(f'This is the last item in the list: {item}')

Or perhaps you'd like to do the opposite:

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    if num_remaining:
        print(f'This is NOT the last item in the list: {item}')

Or, just to know how many remain as you go...

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    print(f'After {item}, there are {num_remaining} items.')

I think the versatility and familiarity with the existing enumerate makes it most Pythonic.

Caveat, unlike enumerate(), rev_enumerate() requires that the input implement __len__, but this includes lists, tuples, dicts and sets just fine.

I have found it convenient to define the loop value before the loop expression. For this box example, match the value in the loop, or wherever else you may need it.

numberofboxes = 1411

for j in range(1,numberofboxes):
    if j != numberofboxes - 1:
        print ("},")
    else:
        print("}")

I've shared two simple methods below to find the end of the loop.

Method 1:

num_list = [1, 2, 3, 4]

for n in num_list:
    if num_list[-1] == n:
        print('this is the last iteration of the loop')

Method 2:

num_list = [1, 2, 3, 4]

loop_count = len(num_list) - 1  # 3
for index, num in enumerate(num_list):
    if index == loop_count:
        print('this is the last iteration of the loop')

I just came across this question and my generic solution uses an Iterator:

from typing import TypeVar, Iterable
E = TypeVar('E')

def metait(i: Iterable[E]) -> Iterable[tuple[E, bool, bool]]:

    first = True
    previous = None
    for elem in i:
        if previous:
            yield previous, first, False
            first = False
        previous = elem

    if previous:
        yield previous, first, True

you will receive a tuple with the original elements and flags for the first and last item. It can be used with every iterable:

d = {'a': (1,2,3), 'b': (4,5,6), 'c': (7,8,9)}

for (k,v), is_first, is_last in metait(d.items()):
    print(f'{k}: {v}  {is_first} {is_last}')

This will give you:

a: (1, 2, 3)  True False
b: (4, 5, 6)  False False
c: (7, 8, 9)  False True
Related