In python, I have a list that should have one and only one truthy value (that is, bool(value) is True). Is there a clever way to check for this
A one-line answer that retains the short-circuiting behavior:
from itertools import ifilter, islice
def only1(l):
return len(list(islice(ifilter(None, l), 2))) == 1
This will be significantly faster than the other alternatives here for very large iterables that have two or more true values relatively early.
ifilter(None, itr) gives an iterable that will only yield truthy elements (x is truthy if bool(x) returns True). islice(itr, 2) gives an iterable that will only yield the first two elements of itr. By converting this to a list and checking that the length is equal to one we can verify that exactly one truthy element exists without needing to check any additional elements after we have found two.
Here are some timing comparisons:
Setup code:
In [1]: from itertools import islice, ifilter
In [2]: def fj(l): return len(list(islice(ifilter(None, l), 2))) == 1
In [3]: def david(l): return sum(bool(e) for e in l) == 1
Exhibiting short-circuit behavior:
In [4]: l = range(1000000)
In [5]: %timeit fj(l)
1000000 loops, best of 3: 1.77 us per loop
In [6]: %timeit david(l)
1 loops, best of 3: 194 ms per loop
Large list where short-circuiting does not occur:
In [7]: l = [0] * 1000000
In [8]: %timeit fj(l)
100 loops, best of 3: 10.2 ms per loop
In [9]: %timeit david(l)
1 loops, best of 3: 189 ms per loop
Small list:
In [10]: l = [0]
In [11]: %timeit fj(l)
1000000 loops, best of 3: 1.77 us per loop
In [12]: %timeit david(l)
1000000 loops, best of 3: 990 ns per loop
So the sum() approach is faster for very small lists, but as the input list gets larger my version is faster even when short-circuiting is not possible. When short-circuiting is possible on a large input, the performance difference is clear.