Python
programming
sequence
search
algorithm

Find first sequence item that matches a criterion

Master System Design with Codemia

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

Introduction

Finding the first item in a sequence that matches a condition is one of the most common “small” programming tasks. In Python, the most idiomatic answers are either a plain for loop or next(...) with a generator expression. The best choice depends on whether you want readability, a default value, or slightly more compact code.

Start With the Simple Loop

The most explicit solution is still a for loop.

python
1def first_match(values, predicate):
2    for value in values:
3        if predicate(value):
4            return value
5    return None
6
7numbers = [4, 7, 10, 15, 23]
8result = first_match(numbers, lambda x: x > 10)
9print(result)  # 15

This is a strong default because it is easy to read, easy to debug, and naturally stops as soon as the first match is found.

The runtime is linear in the worst case, but it short-circuits on the first success, which is exactly what you want.

Use next for an Idiomatic One-Liner

Python also has a compact built-in pattern:

python
numbers = [4, 7, 10, 15, 23]
result = next((x for x in numbers if x > 10), None)
print(result)  # 15

This works because the generator expression produces matching items lazily, and next returns the first one it sees. The second argument to next is the default returned when no match exists.

This is concise and idiomatic, especially when the predicate is small and obvious.

Understand the Default Value

The default argument matters because without it, next raises StopIteration when no item matches.

python
numbers = [1, 2, 3]
result = next((x for x in numbers if x > 10), None)
print(result)  # None

If None is a meaningful value in your data, use a different sentinel instead.

python
1sentinel = object()
2result = next((x for x in numbers if x > 10), sentinel)
3if result is sentinel:
4    print("No match")

That avoids ambiguity between “no match” and “the match is literally None.”

Searching Objects, Not Just Numbers

The same pattern works well for dictionaries, dataclass instances, or any other iterable objects.

python
1users = [
2    {"name": "Ana", "active": False},
3    {"name": "Ben", "active": True},
4    {"name": "Cara", "active": True},
5]
6
7first_active = next((user for user in users if user["active"]), None)
8print(first_active)

This is usually better than filtering the whole sequence when you only need the first matching item.

Avoid Building Unnecessary Lists

A common beginner approach is:

python
matches = [x for x in numbers if x > 10]
result = matches[0] if matches else None

This works, but it scans the whole sequence and allocates a new list even though only the first match is needed. A generator expression or loop is more efficient because it stops early.

That distinction matters more when the sequence is large or when the predicate is expensive.

Consider the Sequence Type

If your data is already sorted or indexed in a special way, there may be faster domain-specific approaches. For example, a sorted numeric list might be searched with bisect, and a dictionary lookup might eliminate iteration entirely.

But for the general problem “find the first item matching a criterion,” iteration is the correct baseline because the criterion can be arbitrary code.

Choose Clarity Over Cleverness

A good rule is:

  • use a for loop when the logic is non-trivial or needs debugging
  • use next((... for ... if ...), default) when the predicate is simple and the code benefits from compactness

Both are idiomatic. Neither is automatically “more Pythonic” in every context.

Common Pitfalls

A common mistake is using a list comprehension when you only need the first result. That wastes work and memory.

Another issue is forgetting the default argument to next, which leads to StopIteration when no match exists.

Developers also sometimes write clever but hard-to-read one-liners for predicates that really belong in a named function. If the condition is complicated, a loop is often clearer.

Finally, be deliberate about the “no match” value. Returning None is fine only if it cannot be mistaken for a valid result.

Summary

  • A plain for loop is the clearest general solution for finding the first matching item.
  • 'next((item for item in sequence if condition), default) is the compact idiomatic alternative.'
  • Use a default value with next unless you explicitly want StopIteration.
  • Prefer lazy search to building a full list of matches.
  • Pick the style that keeps the predicate and fallback behavior easiest to understand.

Course illustration
Course illustration

All Rights Reserved.