I would like to get the first item from a list matching a condition. It\'s important that the resulting method not process the entire list, which could be quite large. For e
Similar to using ifilter
, you could use a generator expression:
>>> (x for x in xrange(10) if x > 5).next()
6
In either case, you probably want to catch StopIteration
though, in case no elements satisfy your condition.
Technically speaking, I suppose you could do something like this:
>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
...
>>> foo
6
It would avoid having to make a try/except
block. But that seems kind of obscure and abusive to the syntax.
By using
(index for index, value in enumerate(the_iterable) if condition(value))
one can check the condition of the value of the first item in the_iterable, and obtain its index without the need to evaluate all of the items in the_iterable.
The complete expression to use is
first_index = next(index for index, value in enumerate(the_iterable) if condition(value))
Here first_index assumes the value of the first value identified in the expression discussed above.
The most efficient way in Python 3 are one of the following (using a similar example):
next(i for i in range(100000000) if i == 1000)
WARNING: The expression works also with Python 2, but in the example is used range
that returns an iterable object in Python 3 instead of a list like Python 2 (if you want to construct an iterable in Python 2 use xrange
instead).
Note that the expression avoid to construct a list in the comprehension expression next([i for ...])
, that would cause to create a list with all the elements before filter the elements, and would cause to process the entire options, instead of stop the iteration once i == 1000
.
next(filter(lambda i: i == 1000, range(100000000)))
WARNING: This doesn't work in Python 2, even replacing range
with xrange
due that filter
create a list instead of a iterator (inefficient), and the next
function only works with iterators.
As mentioned in other responses, you must add a extra-parameter to the function next
if you want to avoid an exception raised when the condition is not fulfilled.
next(filter(lambda i: i == 1000, range(100000000)), False)
With this style you need to surround the comprehension expression with ()
to avoid a SyntaxError: Generator expression must be parenthesized if not sole argument
:
next((i for i in range(100000000) if i == 1000), False)
In Python 3:
a = (None, False, 0, 1)
assert next(filter(None, a)) == 1
In Python 2.6:
a = (None, False, 0, 1)
assert next(iter(filter(None, a))) == 1
EDIT: I thought it was obvious, but apparently not: instead of None
you can pass a function (or a lambda
) with a check for the condition:
a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3
I would write this
next(x for x in xrange(10) if x > 3)
This question already has great answers. I'm only adding my two cents because I landed here trying to find a solution to my own problem, which is very similar to the OP.
If you want to find the INDEX of the first item matching a criteria using generators, you can simply do:
next(index for index, value in enumerate(iterable) if condition)