107

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?

6 Answers 6

196

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
Sign up to request clarification or add additional context in comments.

8 Comments

I'd add a try: except StopIteration: to handle lists containing only None items. But that's a great answer.
@Germano updated the answer, please check. Btw, Jon's answer should be accepted - he was first to suggest a solution for None-only lists.
actually I think Jon's answer is not correct: it only returns True or False, not the first not None item of the list.
@kevlar1818, if you leave out the "is not None", then it treats 0 and "" the same as it would treat a None (at least in my version of python)
Nice to combine with Walrus operator when evaluating functions: next((x for func in funcs if (x:= func(tree))), 'Default')
|
41

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)

3 Comments

Not if there are any values that evaluate to False, such as: 0, '', [], (), False
Thanks for the correction, I overlooked that (updated post)
This is nice and terse. I wanted the first non-empty string.
20

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'

2 Comments

Excellent! A shame this is part of the standard lib.
Well the recipe is part of the docs. You just have to implement it yourself.
12

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.

3 Comments

You can use map instead of a generator expression in python 3. first_non_null = next(filter(bool, map(my_dict.get, my_list)), None)
That’s indeed more elegant, I’ve updated the answer with your suggestion
This will not output falsy values. For example, if calculate = lambda it: it and my_list = [0, False, "", "foo"], your method will return "foo". To filter out only None values, replace filter(bool, ...) with filter(lambda it: it is not None, ...).
7

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.

Comments

5

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.