Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clever any() like function to check if at least n elements are True?

Say I have an iterable (in my case a list):

l = [True, False, False, True]

I know that the easiest and fastest way to check if at least one of those elements is True is simply to use any(l), which will return True.

But what if I want to check that at least two elements are True? My goal is to process it in the fastest way possible.

My code right now looks like this (for two elements):

def check_filter(l):
    if len([i for i in filter(None, l)]) > 1:
        return True
return False

This is about 10 times slower than any(), and does not seem very pythonic to me.

like image 495
LoicM Avatar asked Feb 28 '17 16:02

LoicM


2 Answers

You could simply use an iterator over the sequence and check that any on the iterator returns always True for n-times:

def check(it, num):
    it = iter(it)
    return all(any(it) for _ in range(num))

>>> check([1, 1, 0], 2)
True

>>> check([1, 1, 0], 3)
False

The key point here is that an iterator remembers the position it was last so each any call will start where the last one ended. And wrapping it in all makes sure it exits early as soon as one any is False.

At least performance-wise this should be faster than most other approaches. However at the cost of readability.


If you want to have it even faster than a solution based on map and itertools.repeat can be slightly faster:

from itertools import repeat

def check_map(it, num):
    return all(map(any, repeat(iter(it), num)))

Benchmarked against the other answers:

# Second "True" element is in the last place
lst = [1] + [0]*1000 + [1]

%timeit check_map(lst, 2)  # 10000 loops, best of 3: 20.3 µs per loop
%timeit check(lst, 2)      # 10000 loops, best of 3: 23.5 µs per loop
%timeit many(lst, 2)       # 10000 loops, best of 3: 153 µs per loop
%timeit sum(l) >= 2        # 100000 loops, best of 3: 19.6 µs per loop

# Second "True" element is the second item in the iterable
lst = [1, 1] + [0]*1000

%timeit check_map(lst, 2)  # 100000 loops, best of 3: 3.05 µs per loop
%timeit check(lst, 2)      # 100000 loops, best of 3: 6.39 µs per loop
%timeit many(lst, 2)       # 100000 loops, best of 3: 5.02 µs per loop
%timeit sum(lst) >= 2      # 10000 loops, best of 3: 19.5 µs per loop
like image 186
MSeifert Avatar answered Oct 10 '22 23:10

MSeifert


L = [True, False, False, True]

This does only the needed iterations:

def many(iterable, n):
    if n < 1:
        return True
    counter = 0
    for x in iterable:
        if x:
            counter += 1
            if counter == n:
                return True
    return False

Now:

>>> many(L, 2)
True
like image 20
Mike Müller Avatar answered Oct 10 '22 23:10

Mike Müller