Reputation: 1003
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
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
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
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
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
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
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