Joan Venge
Joan Venge

Reputation: 330992

Finding first and last index of some value in a list in Python

Is there any built-in methods that are part of lists that would give me the first and last index of some value, like:

verts.IndexOf(12.345)
verts.LastIndexOf(12.345)

Upvotes: 114

Views: 256948

Answers (11)

Mateus Alves de Oliveira
Mateus Alves de Oliveira

Reputation: 1031

We can do this:

def rindex(array, value) -> int:
    if value not in array:
        return -1
    i = array[::-1].index(value)
    return len(array) - i - 1

That will not revert array in memory address, then it avoids two array.reverse() calls. And also, avoids ValueError: <value> is not in list

Upvotes: 0

Fabrice salvaire
Fabrice salvaire

Reputation: 41

The right solution is the one that perform a "C level reversed loop match".

In pseudo C code:

for (int i = len(list) -1; i >= 0; i--)
    if list[i] == value:
        return i
raise NotFound

In Python, the right solution should be:

def rindex(lst, value):
    # reversed is a buitin backward iterator
    # operator.indexOf can handle this iterator
    # if value is not Found it raises
    #    ValueError: sequence.index(x): x not in sequence
    _ = operator.indexOf(reversed(lst), value)
    # we must fix the resersed index
    return len(lst) - _ - 1

But it would be better to have a reversed index builtin.

Upvotes: 2

Jacob Phillips
Jacob Phillips

Reputation: 9264

How has nobody suggested to iterate in reverse rather than reversing the array? It's still O(n) because the last occurrence could be at the beginning, but it kind of has to be faster than reversing the list first, no?

def rindex(arr, value):
    for i in range(len(arr)-1, -1, -1):
        if arr[i] == value:
            return i
    return -1

Upvotes: -2

111
111

Reputation: 19

This method can be more optimized than above:

def rindex(iterable, value):
    try:
        return len(iterable) - next(i for i, val in enumerate(reversed(iterable)) if val == value) - 1
    except StopIteration:
        raise ValueError

Upvotes: 1

Vlad Bezden
Vlad Bezden

Reputation: 89557

Using max + enumerate to get last element occurrence

rindex = max(i for i, v in enumerate(your_list) if v == your_value)

Upvotes: 4

Stefan Pochmann
Stefan Pochmann

Reputation: 28596

Perhaps the two most efficient ways to find the last index:

def rindex(lst, value):
    lst.reverse()
    i = lst.index(value)
    lst.reverse()
    return len(lst) - i - 1
def rindex(lst, value):
    return len(lst) - operator.indexOf(reversed(lst), value) - 1

Both take only O(1) extra space and the two in-place reversals of the first solution are much faster than creating a reverse copy. Let's compare it with the other solutions posted previously:

def rindex(lst, value):
    return len(lst) - lst[::-1].index(value) - 1

def rindex(lst, value):
    return len(lst) - next(i for i, val in enumerate(reversed(lst)) if val == value) - 1

Benchmark results, my solutions are the red and green ones: unshuffled, full range

This is for searching a number in a list of a million numbers. The x-axis is for the location of the searched element: 0% means it's at the start of the list, 100% means it's at the end of the list. All solutions are fastest at location 100%, with the two reversed solutions taking pretty much no time for that, the double-reverse solution taking a little time, and the reverse-copy taking a lot of time.

A closer look at the right end: unshuffled, tail part

At location 100%, the reverse-copy solution and the double-reverse solution spend all their time on the reversals (index() is instant), so we see that the two in-place reversals are about seven times as fast as creating the reverse copy.

The above was with lst = list(range(1_000_000, 2_000_001)), which pretty much creates the int objects sequentially in memory, which is extremely cache-friendly. Let's do it again after shuffling the list with random.shuffle(lst) (probably less realistic, but interesting):

shuffled list, full range

shuffled list, tail part

All got a lot slower, as expected. The reverse-copy solution suffers the most, at 100% it now takes about 32 times (!) as long as the double-reverse solution. And the enumerate-solution is now second-fastest only after location 98%.

Overall I like the operator.indexOf solution best, as it's the fastest one for the last half or quarter of all locations, which are perhaps the more interesting locations if you're actually doing rindex for something. And it's only a bit slower than the double-reverse solution in earlier locations.

All benchmarks done with CPython 3.9.0 64-bit on Windows 10 Pro 1903 64-bit.

Upvotes: 47

Eugene Yarmash
Eugene Yarmash

Reputation: 149776

Python lists have the index() method, which you can use to find the position of the first occurrence of an item in a list. Note that list.index() raises ValueError when the item is not present in the list, so you may need to wrap it in try/except:

try:
    idx = lst.index(value)
except ValueError:
    idx = None

To find the position of the last occurrence of an item in a list efficiently (i.e. without creating a reversed intermediate list) you can use this function:

def rindex(lst, value):
    for i, v in enumerate(reversed(lst)):
        if v == value:
            return len(lst) - i - 1  # return the index in the original list
    return None    

print(rindex([1, 2, 3], 3))     # 2
print(rindex([3, 2, 1, 3], 3))  # 3
print(rindex([3, 2, 1, 3], 4))  # None

Upvotes: 9

Qinsheng Zhang
Qinsheng Zhang

Reputation: 1323

s.index(x[, i[, j]])

index of the first occurrence of x in s (at or after index i and before index j)

Upvotes: 0

user2669486
user2669486

Reputation: 339

If you are searching for the index of the last occurrence of myvalue in mylist:

len(mylist) - mylist[::-1].index(myvalue) - 1

Upvotes: 33

SilentGhost
SilentGhost

Reputation: 319601

Sequences have a method index(value) which returns index of first occurrence - in your case this would be verts.index(value).

You can run it on verts[::-1] to find out the last index. Here, this would be len(verts) - 1 - verts[::-1].index(value)

Upvotes: 163

Caridorc
Caridorc

Reputation: 6641

As a small helper function:

def rindex(mylist, myvalue):
    return len(mylist) - mylist[::-1].index(myvalue) - 1

Upvotes: 14

Related Questions