Getting the first non None value from list

后端 未结 5 1621
盖世英雄少女心
盖世英雄少女心 2021-01-31 13:25

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.addr
相关标签:
5条回答
  • 2021-01-31 13:54

    I think this is the simplest way when dealing with a small set of values (will work in a list comprehension as well):

    firstVal = a or b or c or d
    

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

    0 讨论(0)
  • 2021-01-31 14:06

    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
    
    0 讨论(0)
  • 2021-01-31 14:06

    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'
    
    0 讨论(0)
  • 2021-01-31 14:09

    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.

    0 讨论(0)
  • 2021-01-31 14:11

    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, 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.

    0 讨论(0)
提交回复
热议问题