The Python Oracle

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

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Melt

--

Chapters
00:00 Clever Any() Like Function To Check If At Least N Elements Are True?
00:38 Accepted Answer Score 14
01:42 Answer 2 Score 3
01:50 Answer 3 Score 0
02:08 Answer 4 Score 4
02:24 Thank you

--

Full question
https://stackoverflow.com/questions/4251...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #python3x #builtin

#avk47



ACCEPTED ANSWER

Score 14


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



ANSWER 2

Score 4


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



ANSWER 3

Score 3


Use sum:

sum(l) >= 2
# True



ANSWER 4

Score 0


Presumably any goes through the iterable until it finds a element that is True, and then stops.

Your solution scans all of the elements to see if there are at least 2. Instead, it should stop scanning as soon as it finds a second True element.