Getting the first non None value from list

Question:

Given a list, is there a way to get the first non-None value? And, if so, what would be the pythonic way to do so?

For example, I have:

  • a = objA.addreses.country.code
  • b = objB.country.code
  • c = None
  • d = 'CA'

In this case, if a is None, then I would like to get b. If a and b are both None, the I would like to get d.

Currently I am doing something along the lines of (((a or b) or c) or d), is there another way?

Asked By: Pran

||

Answers:

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
Answered By: alecxe

Adapt from the following (you could one-liner it if you wanted):

values = (a, b, c, d)
not_None = (el for el in values if el is not None)
value = next(not_None, None)

This takes the first non None value, or returns None instead.

Answered By: Jon Clements

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'
Answered By: pylang

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)

Answered By: DannyMoshe

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.

Answered By: Lars Blumberg

First of all want to mention that such function exists in SQL and is called coalesce. Found no such thing in Python so made up my own one, using the recipe of @alecxe.

def first_not_none(*values):
    return next((v for v in values if v is not None), None)

Really helps in cases like this:

attr = 'title'
document[attr] = first_not_none(cli_args.get(attr), document_item.get(attr),
                                defaults_item.get(attr), '')
Answered By: Nick Legend
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.