553

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 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. I don't particularly want to copy this function around if I don't have to. Is there a built-in way to get the first item matching a condition?

davidism
  • 121,510
  • 29
  • 395
  • 339
Chris Phillips
  • 11,607
  • 3
  • 34
  • 45
  • 8
    Related: [Getting the **index** of the first item that matches a condition](https://stackoverflow.com/questions/1701211/python-return-the-index-of-the-first-element-of-a-list-which-makes-a-passed-fun) – Aran-Fey May 04 '18 at 18:09

18 Answers18

853

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;-).

Python <= 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.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 7
    Since this is the selected answer, I feel compelled to share an answer to selecting the first element correctly [here](http://stackoverflow.com/a/35370041/916568). In short: usage of next shouldn't be encouraged. – Guy Jul 07 '16 at 10:56
  • 3
    @guyarad how is the solution proposed in that answer less "cryptic" than just using next ? The only argument against next (in that answer) is that you must handle an exception; really ? – Abraham Toledo Jan 24 '18 at 23:34
  • My view is a little different than the time I wrote the comment. I see your point. That's being said, having to handle `StopIteration` is really not pretty. Better use a method. – Guy Jan 26 '18 at 03:25
  • 1
    Does it parse the whole array, or does it stop at the first matching element (very important for efficiency)? – Olivier Pons Jul 13 '20 at 16:28
  • @OlivierPons It stops as soon as it finds a match, if there is one. – CrazyChucky Apr 11 '21 at 17:12
  • Note that this is not idempotent. (Meaning that `next()` will only return the first element the first time it is called). – cowlinator Nov 09 '21 at 02:57
  • @cowlinator No. It is not for `next()` to decide if it will be “idempotent”. It is `the_iterable` which does: if it is a generator, neither `next()` nor any other solution can reset it. If `the_iterable` is a e.g. a list or tuple, then `next()` will be “idempotent”. – Robert Siemer Dec 23 '22 at 05:40
61

Damn Exceptions!

I love Alex Martelli's 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
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Jossef Harush Kadouri
  • 32,361
  • 10
  • 130
  • 129
45

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
Caridorc
  • 6,222
  • 2
  • 31
  • 46
  • 8
    If you are wrapping it with a method, at least catch StopIteration and raise EmptySequence error. Would be much prettier when there are no elements. – Guy Jan 26 '18 at 03:28
  • @guyarad Is that a kind of ValueError? – Caridorc Jan 28 '18 at 11:22
  • no... I just "invented" an exception. If you are implementing a `first` function, having it raise a `StopIteration` exception just seems weird. Anyway, take a look at [boltons.iterutils.first](https://boltons.readthedocs.io/en/latest/iterutils.html#boltons.iterutils.first). – Guy Feb 05 '18 at 08:25
  • 3
    @guyarad `StopIteration` is the canonical "out of elements" exception in python. I don't see a problem with it being thrown. I'd probably use a default of "None" which can be passed in as a default parameter to the function. – Baldrickk May 24 '18 at 07:51
  • 2
    Baldrickk I feel like this isn't an iteration method. You won't call this one in a contest of an iterator. But I'm not feeling too strongly about it :) – Guy May 25 '18 at 12:45
  • 1
    There should be an optional default argument, and if that argument not be supplied, only then raise an exception when no element in the sequence satisfy the condition. – Zorf Feb 26 '20 at 22:39
  • @Zorf I added an alternative version with a default argument – Caridorc Mar 09 '20 at 21:23
  • why would you not just use the built-in default functionality of `next()`? `next(x for x in iterable if condition(x), default)` – brandonscript Apr 10 '21 at 04:39
  • @brandonscript interesting I did not know about that – Caridorc Apr 11 '21 at 23:28
  • Yep! The other thing you'll want to do here is test whether condition exists before trying to write a condition; sometimes even returning x at all from an iterator could be expensive (e.g., if x is a generator yield). So check `if condition` otherwise just return `next(x for x in iterable, default)` – brandonscript Apr 11 '21 at 23:31
27

The most efficient way in Python 3 are one of the following (using a similar example):

With "comprehension" style:

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.

With "functional" style:

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.

Default value

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.

"functional" style:

next(filter(lambda i: i == 1000, range(100000000)), False)

"comprehension" style:

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)
Mariano Ruiz
  • 4,314
  • 2
  • 38
  • 34
24

For anyone using Python 3.8 or newer I recommend using "Assignment Expressions" as described in PEP 572 -- Assignment Expressions.

if any((match := i) > 3 for i in range(10)):
    print(match)
airborne
  • 3,664
  • 4
  • 15
  • 27
14

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.

Matt Anderson
  • 19,311
  • 11
  • 41
  • 57
  • +1: Not obscure, nor abusive. All things considered, the last one seems pretty clean. – S.Lott Mar 02 '10 at 10:56
  • 7
    The last one isn't at all clean—`for foo in genex: break` is just a way of doing `foo = next(genex)` without making the assignment clear and with the exception that would be raised if the operation doesn't make sense being squashed. Ending up with a failure code instead of catching an exception is usually a *bad* thing in Python. – Mike Graham Mar 02 '10 at 16:49
14

I would write this

next(x for x in xrange(10) if x > 3)
Mike Graham
  • 73,987
  • 14
  • 101
  • 130
10

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()
sth
  • 222,467
  • 53
  • 283
  • 367
  • 4
    Generator expressions are simpler. – Eric O. Lebigot Mar 02 '10 at 08:34
  • 2
    (`i`)`filter` and (`i`)`map` can make sense for cases where the functions being applied already exist, but in a situation like this it makes a lot more sense just to use a generator expression. – Mike Graham Mar 02 '10 at 16:53
  • This is the best answer. Avoid list comprehensions http://xahlee.info/comp/list_comprehension.html – mit Oct 29 '18 at 20:54
7

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

(x for x in range(10) if x > 3).next()
Menno Smits
  • 2,074
  • 1
  • 13
  • 12
5

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.

Bill Bell
  • 21,021
  • 5
  • 43
  • 58
blue_note
  • 27,712
  • 9
  • 72
  • 90
4

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)
Daniel
  • 11,332
  • 9
  • 44
  • 72
  • **See also:** https://stackoverflow.com/questions/1701211/python-return-the-index-of-the-first-element-of-a-list-which-makes-a-passed-fun – dreftymac Dec 05 '19 at 21:06
3

The following are 3 alternatives, with benchmarks.

Using next()

The one-liner:

values = list(range(1, 10000000))

value = next((x for x in values if x > 9999999), None)

Using a function

This is an alternative to using next() using a function, it's about 2%-5% faster:

values = list(range(1, 10000000))

def first(items):
    for item in items:
        if item > 9999999:  # Your condition
            return item
    return None  # Default value

value = first(values)

Using lambda

This is a function that can be used for replacing next() in all cases. Performance are about 300% slower:

values = list(range(1, 10000000))

def first(items, condition, default = None):
    for item in items:
        if condition(item):
            return item
    return default

value = first(values, lambda x: x > 9999999, None)

Benchmarks

  • Function: 1x
  • Next: 1.02x-1.05x
  • Lambda: > 3x

Memory consumption is on par.

This is the benchmark.

Davide Muzzarelli
  • 929
  • 1
  • 9
  • 13
2

You could also use the argwhere function in Numpy. For example:

i) Find the first "l" in "helloworld":

import numpy as np
l = list("helloworld") # Create list
i = np.argwhere(np.array(l)=="l") # i = array([[2],[3],[8]])
index_of_first = i.min()

ii) Find first random number > 0.1

import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_first = i.min()

iii) Find the last random number > 0.1

import numpy as np
r = np.random.rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_last = i.max()
aim
  • 657
  • 2
  • 12
  • 26
1

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
Berislav Lopac
  • 16,656
  • 6
  • 71
  • 80
1

here is a speedtest of three ways. Next() is not the fastest way.

from timeit import default_timer as timer

# Is set irreflexive?

def a():
    return frozenset((x3, x3) for x3 in set([x1[x2] for x2 in range(2) for x1 in value]) if (x3, x3) in value) == frozenset()


def b():
    return next((False for x1 in value if (x1[0], x1[0]) in value or (x1[1], x1[1]) in value), True)


def c():
    for x1 in value:
        if (x1[0], x1[0]) in value or (x1[1], x1[1]) in value:
            return False
    return True


times = 1000000
value = frozenset({(1, 3), (2, 1)})


start_time = timer()
for x in range(times):
    a()
print("a(): Calculation ended after " + str(round((timer() - start_time) * 1000) / 1000.0) + " sec")

start_time = timer()
for x in range(times):
    b()
print("b(): Calculation ended after " + str(round((timer() - start_time) * 1000) / 1000.0) + " sec")

start_time = timer()
for x in range(times):
    c()
print("c(): Calculation ended after " + str(round((timer() - start_time) * 1000) / 1000.0) + " sec")

Results to:

Calculation ended after 1.365 sec
Calculation ended after 0.685 sec
Calculation ended after 0.493 sec
Lukas
  • 446
  • 3
  • 11
0

I know it is too late but still, here is my answer:

def find_index(nums, fn):
    return next(i for i, x in enumerate(nums) if fn(x))
print(find_index([1, 2, 3, 4], lambda n: n % 2 == 1))
Itachi
  • 1
  • 1
  • Please see [how to answer](https://stackoverflow.com/help/how-to-answer). Your answer does not address the actual question posed by OP, which is how to get the first *item*, not the *index* of the first item. And you provide no additional value to the thread, as other answers have already demonstrated how to utilize `next()` and lambda functions. – AlexK Sep 21 '22 at 21:27
0

If you don't want to use next() you can use unpacking:

>>> a, *_ = filter(lambda e: e == 10, [7,8,9,10,11,12])
>>> a
10
>>> _
[]
>>> a, *_ = filter(lambda e: e == 1000, [7,8,9,10,11,12])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected at least 1, got 0)

Note that using filter() is equivalent to writing (item for item in iterable if condition) Python Docs.

If you need support for the edge case you can write like this:

>>> a, *_ = [e for e in [7,8,9,10,11,12] if e == 1000] or [None]
>>> a
None
>>> _
[]
JakeTheSnake
  • 2,456
  • 3
  • 14
  • 26
-3

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.

Mizipzor
  • 51,151
  • 22
  • 97
  • 138