What is the best way to get the first item from an iterable matching a condition?

In Python, I would like to get the first item from a list matching a condition. For example, the following function is adequate:

def first(the_iterable, condition = lambda x: True):
    for i in the_iterable:
        if condition(i):
            return i

This function could be used something like this:

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4

However, I can't think of a good built-in / one-liner to let me do this (and I don't particularly want to copy this function around if I don't have to). Any ideas?

(It's important that the resulting method not process the entire list, which could be quite large.)

Answers


In Python 2.6 or better:

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

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

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

    if you want default_value (e.g. None) to be returned instead. Note that you need an extra pair of parentheses around the generator expression in this case - they are needed always when 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.


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.


The itertools module contains a filter function for iterators. The first element of the filtered iterator can be obtained by calling next() on it:

from itertools import ifilter

print ifilter((lambda i: i > 3), range(10)).next()

For older versions of Python where the next built-in doesn't exist:

(x for x in range(10) if x > 3).next()

I would write this

next(x for x in xrange(10) if x > 3)

Since you've requested a built-in one-liner, this will avoid the issue of a StopIteration exception, though it requires that your iterable is small so you can cast it to a list, since that is the only construct I know of which will swallow a StopIteration and let you peek at the values:

(lambda x:x[0] if x else None)(list(y for y in ITERABLE if CONDITION))

(If no element matches, you will get None rather than a StopIteration exception.)


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) if any(a) else 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))

Oneliner:

thefirst = [i for i in range(10) if i > 3][0]

If youre not sure that any element will be valid according to the criteria, you should enclose this with try/except since that [0] can raise an IndexError.


Need Your Help

ASP.NET MVC Session with ID in custom header

c# asp.net asp.net-mvc session-state

Is it possible in ASP.NET MVC (without writing toooo much code) to manually select what the current sessionId is? I don't mean like creating my own sessionId, but rather I want to store the session...

Need help in using Mechanize module in Python script

python python-2.7 mechanize mechanize-python

I am learning to parse the data using mechanize but I have got a problem in the script:

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.