Home > database >  How to remove all instances of None from the end of a list?
How to remove all instances of None from the end of a list?

Time:02-22

Python has a string method called rstrip():

>>> s = "hello world!!!"
>>> s.rstrip("!")
'hello world'

I want to implement similar functionality for a Python list. That is, I want to remove all instances of a given value from the end of a list. In this case, the value is None.

Here's some starting examples:

[1, 2, 3, None]
[1, 2, 3, None, None, None]
[1, 2, 3, None, 4, 5]
[1, 2, 3, None, None, 4, 5, None, None]

I want the end results to be:

[1, 2, 3]
[1, 2, 3]
[1, 2, 3, None, 4, 5]
[1, 2, 3, None, None, 4, 5]

Here's my solution so far:

while l[-1] is None:
    l.pop()

CodePudding user response:

If you want to modify the list in-place, then your solution is good, just make sure you handle the case when the list is empty:

while l and l[-1] is None:
    l.pop()

If you want to compute a new list, you can adapt your solution into:

def stripNone(l):
    if not l:
        return []
    
    rlim = 0
    for x in reversed(l):
        if x is None:
            rlim  = 1
        else:
            break
    
    return l[: len(l) - rlim]

There is also itertools.dropwhile, but you have to perform two reversals:

def stripNone(l):
    return list(dropwhile(lambda x: x is None, l[::-1]))[::-1]

CodePudding user response:

Two more versions that also work for None-only lists:

while None in l[-1:]:
    l.pop()
for x in reversed(l):
    if x is not None:
        break
    l.pop()

Benchmarking some solutions on l = [None] * 10**6:

 82 ms  stripNone1
136 ms  stripNone2
 60 ms  stripNone3
 42 ms  stripNone3b
 55 ms  stripNone4

Benchmark code:

from timeit import repeat

def stripNone1(l):
    while l and l[-1] is None:
        l.pop()

def stripNone2(l):
    while None in l[-1:]:
        l.pop()

def stripNone3(l):
    for x in reversed(l):
        if x is not None:
            break
        l.pop()

def stripNone3b(l):
    pop = l.pop
    for x in reversed(l):
        if x is not None:
            break
        pop()

def stripNone4(l):
    for i, x in enumerate(reversed(l), 1):
        if x is not None:
            del l[-i:]
            break

solutions = stripNone1, stripNone2, stripNone3, stripNone3b, stripNone4
for i in range(3):
    print(f'Round {i 1}:')
    for sol in solutions:
        ls = [[None] * 10**6 for _ in range(5)]
        time = min(repeat(lambda: sol(ls.pop()), number=1))
        print(f'{int(time * 1000):3d} ms  {sol.__name__}')
  • Related