Reputation: 83253
What is the Pythonic way to check if a iterables's length is at least n
?
This is my approach:
import itertools
def is_iterable_longer_than(iterable, n):
return n <= len(itertools.islice(iterable, n))
Is there anything better?
EDIT:
I am willing to consume the iterable, even if it it can be evaluated only once.
As pointed out, the above has an error. It should be:
return n <= len(list(itertools.islice(iterable, n)))
Upvotes: 1
Views: 214
Reputation: 280867
There's no general way. Yours doesn't even work:
>>> def is_iterable_longer_than(iterable, n):
... return n <= len(itertools.islice(iterable, n))
...
>>> is_iterable_longer_than([], 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in is_iterable_longer_than
TypeError: object of type 'itertools.islice' has no len()
The only way to tell if an iterable has at least n
objects in it is to iterate over it until you get n
objects or run out. Unfortunately, some iterables can only be iterated over once. If you don't care about using the contents of the iterable, you can do this:
def is_iterable_longer_than(iterable, n):
return n == sum(1 for _ in itertools.islice(iterable, n))
If you need to use the contents of the iterable, you can create another iterable that looks like the original:
def is_iterable_longer_than(iterable, n):
iter1, iter2 = itertools.tee(iterable)
return sum(1 for _ in itertools.islice(iter1, n)) == n, iter2
While we're at it, we might as well try len
, just in case it works:
def is_iterable_longer_than(iterable, n):
iter1, iter2 = itertools.tee(iterable)
try:
return len(iterable) >= n, iter2
except TypeError:
return sum(1 for _ in itertools.islice(iter1, n)) == n, iter2
Upvotes: 2
Reputation: 526643
It depends on whether you're willing to evaluate the generator or not.
If you are, it's trivial:
def gen_length_was_at_least_n(gen, n):
return n == sum(1 for _ in itertools.islice(gen, n))
If you're not, then you're stuck, unless you're willing to evaluate it but keep the values buffered via tee
:
gen, extra = itertools.tee(gen)
if gen_length_was_at_least_n(extra):
# ... do something with gen
Note that this does partially evaluate the original iterator; it just keeps the values around and serves them through the new generator returned from tee
. That means if evaluating the generator has side effects, they'll be triggered when you do the length check.
Upvotes: 1