2

In Python you can do

print (0 or None or False or "" or [] or "hello" or None or "bar")

which will print

hello

Can you do the same with a list? I.e. is there a Python function foo so that the following will also print hello?

print (foo([0, None, False, "", [], "hello", None, "bar"]))

Note that bar is not printed.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
oyvind
  • 1,429
  • 3
  • 14
  • 24
  • Possible duplicate of [Finding the first list element for which a condition is true](https://stackoverflow.com/questions/16962994/finding-the-first-list-element-for-which-a-condition-is-true) – Mykola Zotko Jan 30 '19 at 14:18

2 Answers2

6

You can use next(filter(None, ...)) or next(filter(bool, ...)) to find the first truthy value from a list:

def foo(l):
    return next(filter(None, l))

The filter() function takes both a filter function, and an iterable, and returns an iterator that of the values from the iterable that pass the filter.

But when you set the filter function to None, then it is essentially the same thing as using bool as the filter function, so only values that are true are allowed through. The next() function then gives you the first such value.

Demo:

>>> def foo(l):
...     return next(filter(None, l))
...
>>> print(foo([0, None, False, "", [], "hello", None, "bar"]))
hello

You may want to add the last value in l as a default for the next() call, in case there are only falsey values; v1 or v2 or v3 or v4 would at least produce v4 if none of the values are truthy, so the following does too:

def foo(l):
    return next(filter(None, l), l[-1])

Using filter(None, ...) is a fraction faster than filter(bool, ...) because the filter.__next__ implementation tests for None before it tests for bool; this speed difference is very small and barely measurable (within a margin of error):

>>> import timeit
>>> import random
>>> t = [random.choice([True, False]) for _ in range(10 ** 6)]
>>> for ff in ('bool', 'None'):
...     count, total = timeit.Timer(f'deque(filter({ff}, t), maxlen=0)', 'from collections import deque; from __main__ import t').autorange()
...     print(f"{ff}: {total / count * 1000:8.4f} ms")
...
bool:  98.9971 ms
None:  95.7907 ms
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I always get told I should use `filter(bool, ...)` instead of `filter(None, ...)` because it's more transparent. – khelwood Jan 30 '19 at 12:25
  • @khelwood: `None` is literally the same thing as `bool`, actually, in the current implementation. Except that the test that checks if the first argument is `None` or `bool` is made each `__next__` iteration and if you use `None` then that test is *fractionaly* faster, resulting in a few % speed advantage for `filter(None, ...)`. It's not going to cost you more than a few microseconds per million filter iterations, so using `filter(bool, ...)` for documentation clarity is still a good idea. – Martijn Pieters Jan 30 '19 at 12:34
0

You can use the function reduce() with the operator or in the lambda function:

from functools import reduce, partial

foo = partial(reduce, lambda x, y: x or y)

print(foo([0, None, False, "", [], "hello", None, "bar"]))
# hello
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73