user1159290
user1159290

Reputation: 1003

Get the highest index with a defined value in an array in python

what could be a nice one-liner to get the highest index in a python array whose value is defined (i.e. not None):

f( [None, 1, 5, None, 3, None, None] )

would return:4 (as the "last" defined element is 4. (with value 3))

Sure a search loop would make the job, but it feels non optimal...

Upvotes: 1

Views: 491

Answers (6)

Kasravnd
Kasravnd

Reputation: 107347

Loop over the reversed of the list and return the first valid item's index:

In [70]: next((len(l) - i for i, j in enumerate(l[::-1], 1) if j is not None), 'NOT FOUND')
Out[70]: 4

Note that since you are looping over the reversed array the correct index would be len(l) - i (if we consider the first index as 1).

If you are looking for a functional and/or more optimized approach you can use numpy and it's where function:

In [1]: import numpy as np

In [2]: lst = [None, 1, 5, None, 3, None, None]

In [4]: np.where(lst)[0][-1]
Out[4]: 4

Upvotes: 2

jonrsharpe
jonrsharpe

Reputation: 122151

If the input list is very large, you can use itertools and the built-in reversed to avoid iterating or copying the whole thing:

from itertools import dropwhile

def last_non_none(seq):
    return next(dropwhile(lambda x: x is None, reversed(seq)))

This will throw StopIteration if there are no non-None values, which I would argue is more pythonic than returning a default.

Upvotes: 0

Moinuddin Quadri
Moinuddin Quadri

Reputation: 48120

Another alternative using filter().

>>> my_list = [None, 1, 5, None, 3, None, None]
>>> filter(lambda x: x[1] is not None, enumerate(my_list))[-1][0]
4

But this won't work for empty list or list with all None. But in order to handle that, we can use and and or statements (since you need one line solution):

>>> (filter(lambda x: x is not None, my_list) and filter(lambda x: x[1] is not None, enumerate(my_list))[-1][0]) or -1
4

This expression will return -1 for the above mentioned edge cases.

Upvotes: 2

jmd_dk
jmd_dk

Reputation: 13120

A relatively Pythonic solution which does not use indices:

a = [None, 1, 5, None, 3, None, None]
index = next(i for i, j in reversed(tuple(enumerate(a))) if j)

The tuple bugs me, but it is needed as reversed cannot take in a generator.

Upvotes: 2

Moses Koledoye
Moses Koledoye

Reputation: 78554

You could create a generator that iterates on the indices of the list in reverse and tests until a not None value object is reached:

def f(lst):
    try:
        return next(i for i in range(len(lst)-1, 0, -1) if lst[i] is not None)
    except StopIteration:
        print 'no item was found'
        raise

Upvotes: 1

Shasha99
Shasha99

Reputation: 1916

lst = [None, 1, 5, None, 3, None, None]

# Nothing will be printed if all elements are None.
print max(i for i,num in enumerate(lst) if num is not None)

Upvotes: 3

Related Questions