Getting the first non None value from list

first_true is an itertools recipe found in the Python 3 docs:

def first_true(iterable, default=False, pred=None):
    """Returns the first true value in the iterable.

    If no true value is found, returns *default*

    If *pred* is not None, returns the first item
    for which pred(item) is true.

    """
    # first_true([a,b,c], x) --> a or b or c or x
    # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
    return next(filter(pred, iterable), default)

One may choose to implement the latter recipe or import more_itertools, a library that ships with itertools recipes and more:

> pip install more_itertools

Use:

import more_itertools as mit

a = [None, None, None, 1, 2, 3, 4, 5]
mit.first_true(a, pred=lambda x: x is not None)
# 1

a = [None, None, None]
mit.first_true(a, default="All are None", pred=lambda x: x is not None)
# 'All are None'

Why use the predicate?

"First non-None" item is not the same as "first True" item, e.g. [None, None, 0] where 0 is the first non-None, but it is not the first True item. The predicate allows first_true to be useable, ensuring any first seen, non-None, falsey item in the iterable is still returned (e.g. 0, False) instead of the default.

a = [None, None, None, False]
mit.first_true(a, default="All are None", pred=lambda x: x is not None)
# 'False'

I think this is the simplest way when dealing with a small set of values:

firstVal = a or b or c or d

Will always return the first non "Falsey" value which works in some cases (given you dont expect any values which could evaluate to false as @GrannyAching points out below)


When the items in your list are expensive to calculate such as in

first_non_null = next((calculate(x) for x in my_list if calculate(x)), None)

# or, when receiving possibly None-values from a dictionary for each list item:

first_non_null = next((my_dict[x] for x in my_list if my_dict.get(x)), None)

then you might want to avoid the repetitive calculation and simplify to:

first_non_null = next(filter(bool, map(calculate, my_list)), None)

# or:

first_non_null = next(filter(bool, map(my_dict.get, my_list)), None)

Thanks to the usage of a generator expression, the calculations are only executed for the first items until a truthy value is generated.


You can use next():

>>> a = [None, None, None, 1, 2, 3, 4, 5]
>>> next(item for item in a if item is not None)
1

If the list contains only Nones, it will throw StopIteration exception. If you want to have a default value in this case, do this:

>>> a = [None, None, None]
>>> next((item for item in a if item is not None), 'All are Nones')
All are Nones

Tags:

Python

List