I get data in the following format:
[-2, -2, 0, 0, 0, 0, 0]
[-2, 20, -1, 0, 3, 0, 0]
with each line being a different input. The lists could be longer than 7 elements. I need to return the index position of the last non-zero element, so:
[-2, -2, 0, 0, 0, 0, 0]
>>> 1
[-2, 20, -1, 0, 3, 0, 0]
>>> 4
The following code does this most of the time:
def getIndex(list):
for element in reversed(list):
if element != 0:
return list.index(element)
However, it doesn't work when there are two of the same numbers, as in the first example above, which returns 0
because -2
is in both the 0th and 1st position of the list.
So how to get the index of the last non-zero element of a list, even when there are elements with the same value?
You can iterate the reversed
list and return the first item which is not a zero, like this
>>> def get_last_non_zero_index(d, default=None):
... rev = (len(d) - idx for idx, item in enumerate(reversed(d), 1) if item)
... return next(rev, default)
...
>>> print(get_last_non_zero_index([-2, -2, 0, 0, 0, 0, 0]))
1
>>> print(get_last_non_zero_index([-2, 20, -1, 0, 3, 0, 0]))
4
>>> print(get_last_non_zero_index([0, 0, 0]))
None
>>> print(get_last_non_zero_index([]))
None
The default
value will be returned if the function cannot find a non-zero value.
Your solution changes the list constantly, I think even the naive for
loop is better:
last_nonzero_index = None
for idx, item in enumerate(originalList):
if item != 0:
last_nonzero_index = idx
List comprehension does the trick:
a = [-2, 20, -1, 0, 3, 0, 0]
ls = [i for i, e in enumerate(a) if e != 0]
print(ls)
output:
[0, 1, 2, 4]
here is a version purely made of generators: none of your lists are copied.
a = [-2, -2, 0, 0, 0, 0, 0]
b = [-2, 20, -1, 0, 3, 0, 0]
def index_of_last_nonzero(lst):
for i, value in enumerate(reversed(lst)):
if value != 0:
return len(lst)-i-1
return -1
print(index_of_last_nonzero(lst=a)) # -> 1
print(index_of_last_nonzero(lst=b)) # -> 4
the idea is to iterate over the reversed
list while counting (with enumerate
). if the list is all zero -1
is returned.
This question is tagged with numpy
but I don't see the obvious solution:
numpy.max(numpy.nonzero(list))
Example with the lists provided:
>>> import numpy as np
>>> np.max(np.nonzero([-2, -2, 0, 0, 0, 0, 0]))
1
>>> np.max(np.nonzero([-2, 20, -1, 0, 3, 0, 0]))
4
The following solution passes all of my test cases:
def getLastNonZeroIndex(originalList):
# Create a new list from the old list
# so changes don't change the old list
newList = list(originalList)
# Keep checking to see if last item equals 0
# If it does, remove it from newList
while newList[-1] == 0:
newList.pop()
# Once the item at newList[-1] != 0
# Return index of the last element
return len(newList)-1
I still suspect there's a better way to do this, so please share any alternative solutions you might have.
Here is a pythonic and optimized approach using itertools.takewhile()
:
from itertools import takewhile
from operator import not_
len(lst) - sum(1 for _ in takewhile(not_, reversed(lst))) - 1
Demo:
In [115]: lst = [-2, -2, 0, 0, 0, 0, 0]
In [118]: len(lst) - sum(1 for _ in takewhile(lambda x : not bool(x), lst[::-1])) - 1
Out[118]: 1
In [119]: lst = [-2, 20, -1, 0, 3, 0, 0]
In [120]: len(lst) - sum(1 for _ in takewhile(lambda x : not bool(x), lst[::-1])) - 1
Out[120]: 4
Here is a benchmarck with another answers on a longer list:
In [141]: lst = lst * 1000
In [142]: %timeit len(lst) - len(list(takewhile(not_, reversed(lst)))) - 1
1000000 loops, best of 3: 758 ns per loop
In [143]: %timeit get_last_non_zero_index(lst)
1000000 loops, best of 3: 949 ns per loop
In [146]: %timeit index_of_last_nonzero(lst)
1000000 loops, best of 3: 590 ns per loop
n_l = []
for i,v in enumerate(l): #enumerate returns the index - i, and value-v
if v != 0:
n_l.append(i) #keep the index if the value is not 0
n_l = sorted(n_l) #we then sort the list of indecies from least to greatest. the greatest index will be the last
greatest_non_zero_index = n_l[-1] #grab the last index
Heres another way to do it in one line!
greatest_non_zero_index = sorted([i for i,v in enumerate(l) if v != 0])[-1]
The following should work:
def last_non_zero_index(my_list):
temp = my_list[:]
while temp[-1] == 0:
del temp[-1]
return len(temp) - 1
Output:
>>> my_list = [-2, -2, 0, 0, 0, 0, 0]
>>> last_non_zero_index(my_list)
1
>>> my_list = [-2, 20, -1, 0, 3, 0, 0]
>>> last_non_zero_index(my_list)
4
Another way for a oneliner with list comprehension is by finding all ocurences of 0 and find the highest index with max()
like this:
a = [1,-2,3,0,2,0,-2,0,0,0]
def get_last_non_zero_idx(my_list):
return max([index for index, el in enumerate(my_list) if el])
print(get_last_non_zero_idx(a))
>> 6
This is running in complexity O(n) as enumerate and max have O(n) runtime.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.