Get the first item from an iterable that matches a condition Get the first item from an iterable that matches a condition python python

Get the first item from an iterable that matches a condition


In Python 2.6+ and Python 3:

If you want StopIteration to be raised if no matching element is found:

next(x for x in the_iterable if x > 3)

If you want default_value (e.g. None) to be returned instead:

next((x for x in the_iterable if x > 3), default_value)

Note that you need an extra pair of parentheses around the generator expression in this case − they are needed whenever the generator expression isn't the only argument.

I see most answers resolutely ignore the next built-in and so I assume that for some mysterious reason they're 100% focused on versions 2.5 and older -- without mentioning the Python-version issue (but then I don't see that mention in the answers that do mention the next built-in, which is why I thought it necessary to provide an answer myself -- at least the "correct version" issue gets on record this way;-).

In 2.5, the .next() method of iterators immediately raises StopIteration if the iterator immediately finishes -- i.e., for your use case, if no item in the iterable satisfies the condition. If you don't care (i.e., you know there must be at least one satisfactory item) then just use .next() (best on a genexp, line for the next built-in in Python 2.6 and better).

If you do care, wrapping things in a function as you had first indicated in your Q seems best, and while the function implementation you proposed is just fine, you could alternatively use itertools, a for...: break loop, or a genexp, or a try/except StopIteration as the function's body, as various answers suggested. There's not much added value in any of these alternatives so I'd go for the starkly-simple version you first proposed.


Damn Exceptions!

I love this answer. However, since next() raise a StopIteration exception when there are no items,i would use the following snippet to avoid an exception:

a = []item = next((x for x in a), None)

For example,

a = []item = next(x for x in a)

Will raise a StopIteration exception;

Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration


As a reusable, documented and tested function

def first(iterable, condition = lambda x: True):    """    Returns the first item in the `iterable` that    satisfies the `condition`.    If the condition is not given, returns the first item of    the iterable.    Raises `StopIteration` if no item satysfing the condition is found.    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)    2    >>> first(range(3, 100))    3    >>> first( () )    Traceback (most recent call last):    ...    StopIteration    """    return next(x for x in iterable if condition(x))

Version with default argument

@zorf suggested a version of this function where you can have a predefined return value if the iterable is empty or has no items matching the condition:

def first(iterable, default = None, condition = lambda x: True):    """    Returns the first item in the `iterable` that    satisfies the `condition`.    If the condition is not given, returns the first item of    the iterable.    If the `default` argument is given and the iterable is empty,    or if it has no items matching the condition, the `default` argument    is returned if it matches the condition.    The `default` argument being None is the same as it not being given.    Raises `StopIteration` if no item satisfying the condition is found    and default is not given or doesn't satisfy the condition.    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)    2    >>> first(range(3, 100))    3    >>> first( () )    Traceback (most recent call last):    ...    StopIteration    >>> first([], default=1)    1    >>> first([], default=1, condition=lambda x: x % 2 == 0)    Traceback (most recent call last):    ...    StopIteration    >>> first([1,3,5], default=1, condition=lambda x: x % 2 == 0)    Traceback (most recent call last):    ...    StopIteration    """    try:        return next(x for x in iterable if condition(x))    except StopIteration:        if default is not None and condition(default):            return default        else:            raise