oyvind
oyvind

Reputation: 1539

Python OR for list

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.

Upvotes: 2

Views: 396

Answers (2)

Mykola Zotko
Mykola Zotko

Reputation: 17834

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

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1123002

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

Upvotes: 6

Related Questions