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)]

1 Like
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!

2 Likes

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…

1 Like

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:

1 Like

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:

1 Like
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!

1 Like

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.

1 Like

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)
1 Like
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!

3 Likes

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.

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

Attempt This Online!

4 Likes