Python
Iterables
List Comprehension
Conditional Selection
Programming Tips

Get the first item from an iterable that matches a condition

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A very common Python task is: find the first element in an iterable that satisfies some condition. The most Pythonic solution is usually next with a generator expression, because it stops as soon as a match is found and does not build an unnecessary intermediate list.

The key idea is to treat the problem as "consume until the first match" rather than "filter everything and then take the first result."

The Standard Pattern

The standard idiom is:

python
match = next((x for x in iterable if condition(x)), default_value)

Example:

python
1def first_even(values):
2    return next((x for x in values if x % 2 == 0), None)
3
4print(first_even([1, 3, 7, 8, 10]))

This returns 8. If no match exists, it returns None because that was the default value supplied to next.

Why A Generator Expression Is Better Than A List

A beginner solution is often:

python
matches = [x for x in values if x % 2 == 0]
first = matches[0]

That works, but it does extra work. It scans the whole iterable and stores every match even though only the first one matters. The generator-expression version stops immediately after the first match, which is both faster and more memory-efficient on large iterables.

A Plain Loop Is Also Fine

If you want maximum readability or more custom logic, a simple loop is still a strong option:

python
1def first_match(iterable, predicate):
2    for item in iterable:
3        if predicate(item):
4            return item
5    return None

This is explicit, easy to debug, and avoids the compactness that some teams find harder to read in nested generator expressions.

Iterables Versus Sequences

The pattern works for any iterable, not just lists. That includes generators, file objects, sets, and dictionary views. This is one reason next is such a good fit: it naturally consumes iterators in the way they are designed to be used.

python
1def numbers():
2    for i in range(100):
3        yield i
4
5print(next((x for x in numbers() if x > 10 and x % 7 == 0), None))

The generator stops as soon as it reaches the first matching value.

Choosing A Default Value

The default you pass to next should match the calling code. None is common, but sometimes a sentinel object or explicit exception is better because it makes "not found" impossible to confuse with a legitimate element from the iterable.

When the source is an iterator rather than a reusable container, searching for the first match consumes items on the way to that match. That behavior is usually exactly what you want, but it matters if later code expects to iterate from the beginning again.

Common Pitfalls

  • Building a full list of matches when only the first result is needed.
  • Calling next(generator) without a default and getting StopIteration when no match exists.
  • Assuming the iterable can be reused after partially consuming a generator.
  • Writing a condition with side effects that makes the search hard to reason about.
  • Forgetting that sets and dictionaries do not preserve the same notion of order as lists in every context.

Summary

  • The idiomatic Python pattern is next((x for x in iterable if predicate(x)), default).
  • It stops at the first match and avoids building an unnecessary list.
  • A plain for loop is also a clean and readable solution.
  • Supplying a default avoids StopIteration when no match exists.
  • The pattern works with any iterable, including generators.

Course illustration
Course illustration

All Rights Reserved.