Getting the first non None value from list

PythonList

Python Problem Overview


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?

Python Solutions


Solution 1 - Python

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

Solution 2 - Python

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)

Solution 3 - Python

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'

Solution 4 - Python

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.

Solution 5 - Python

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.

Solution 6 - Python

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), '')

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPranView Question on Stackoverflow
Solution 1 - PythonalecxeView Answer on Stackoverflow
Solution 2 - PythonDannyMosheView Answer on Stackoverflow
Solution 3 - PythonpylangView Answer on Stackoverflow
Solution 4 - PythonLars BlumbergView Answer on Stackoverflow
Solution 5 - PythonJon ClementsView Answer on Stackoverflow
Solution 6 - PythonNick LegendView Answer on Stackoverflow