Find index of last non-None element in list [code golfing]

Consider a list L: list[int | None]. Find idx such that isinstance(L[idx], int) and all(i is None for i in L[idx + 1 :]) are both True.

My solution:

idx = [i for i, e in enumerate(L) if e is not None][-1]

For extra credit, idx should be 0 if all(i is None for i in L).

Your specification is incomplete. For cases without non-None elements, it’s impossible. (You cover that in the “extra credit” version, but not in the main version.)

def ints(L):
    return [i for i, e in [(0, 0)] + list(enumerate(L)) if e is not None][-1]

Returns 0 if L is empty or if it contains only Nones.

Does lint apply to golfing? e != None is shorter than is not None

Oh and this works

[(0, 0), *enumerate(L)]

32 (original is 49)
True 49 [i for i, e in enumerate(L) if e is not None][-1]
True 40 max(i for i,x in enumerate(L)if x!=None)
True 39 len(L)+~[*map(type,L)][::-1].index(int)
True 32 ~[*map(type,L)][::-1].index(int)

Tester:

L = [None, 3, None, 1, None, 4, 0, None, None, None, None]

codes = '''\
[i for i, e in enumerate(L) if e is not None][-1]
max(i for i,x in enumerate(L)if x!=None)
len(L)+~[*map(type,L)][::-1].index(int)
~[*map(type,L)][::-1].index(int)
'''.splitlines()

for c in codes:
    idx = eval(c)
    correct = isinstance(L[idx], int) and all(i is None for i in L[idx + 1 :])
    print(correct, len(c.strip()), c)

Attempt This Online!

Note that this result does not distinguish between a case where only the element at index 0 is not None and a case where all elements are None.

What should be the result if the list is empty?

I don’t think this works? I get -5 on your test. I guess the point is that this is still technically correct, just not the index you might expect


43 chars, 50 with bonus (was 44/51; removed a space)

''.join('ab'[l==None]for l in L).rfind('a')  # -1 if L is all None
max(0,''.join('ab'[l==None]for l in L).rfind('a'))  # 0 if L is all None

I don’t think I’m going to beat @Stefan2’s, though. That is some perl-level golf. :slight_smile:

Argh, it actually sometimes “fails” the test as written, for example for L = [3], since I compute -1 and then all(i is None for i in L[idx + 1 :]) is false. But I claim the test is wrong and the index is correct :stuck_out_tongue:

Extensive tester, with adjusted test

Tests all (solvable) cases with up to 10 elements of None, 0 and 1.

Using all(i is None for i in L[idx + 1 or len(L):]) (added or len(L)), to accept idx = -1 when that’s where the last non-None is.

from itertools import product

codes = '''\
[i for i, e in enumerate(L) if e is not None][-1]
max(i for i,x in enumerate(L)if x!=None)
len(L)+~[*map(type,L)][::-1].index(int)
~[*map(type,L)][::-1].index(int)
'''.splitlines()

Ls = []
for n in range(11):
    it = product((None, 0, 1), repeat=n)
    next(it)
    Ls += it

for c in codes:
    cc = compile(c, '', 'eval')
    correct = all(
        isinstance(L[idx], int) and all(i is None for i in L[idx + 1 or len(L):])
        for L in Ls
        for idx in [eval(cc)]
    )
    print(correct, len(c.strip()), c)

Attempt This Online!

I think that’s already covered by the ‘extra credits’ case. If L==[] then all(i is None for i in L) is True.

Traversing from the back should always win (regarding number of operations). This needs to be done in a function call so we can return immediately rather than finish entire traversal

def find_not_none(L: list[int | None]):
    for i, val in enumerate(reversed(L)):
        if val is not None:
            return len(L) - 1 - i
    return 0 # For your extra credit

idx = find_not_none(L)

Code golfing isn’t about number of operations, though. Frequently the winners are rather the opposite, brute force solutions using huge numbers of operations even when few would suffice.

Ah I actually did not know what is code golfing. I assumed it is another version of LeetCode challenge :rofl:

Not really:

idx = next((len(L) - 1 - i for i, val in enumerate(reversed(L)) if val is not None), 0)
Destructive 28

I think usually that’s allowed.

Not counting the idx= (since I also didn’t previously, and no real golfer would use such a long variable name anyway :-)).

while L.pop()==None:0
idx=len(L)

Attempt This Online!

Although this would fail on [] or [None]

Yes, I’m only doing the main version, not the "extra credit” version.

The list must contain a non-None value, otherwise the task would’ve specified what to do if that’s not the case. And Alexander’s original code, declared to be a solution, likewise “fails” on such unsolvable input. Put differently, as it’s not specified what to do in such cases, anything is allowed with them, so it’s not failing.

34 for the extra credit version
str(L).strip(', None]').count(',')

Attempt This Online!