python
list comprehension
indexing
conditional logic
programming tips

Finding the index of elements based on a condition using python list comprehension

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Finding element positions that satisfy a condition is a routine Python task in parsing, validation, and feature engineering. The cleanest pattern for most cases is list comprehension with enumerate, because it gives both index and value in one pass. Choosing the right variant for first match, all matches, or nested data keeps code readable and efficient.

Core Pattern with enumerate

When you want all matching indices, use enumerate inside a list comprehension.

python
numbers = [12, 7, 5, 18, 7, 21, 7]
indices = [i for i, value in enumerate(numbers) if value == 7]
print(indices)

Output:

text
[1, 4, 6]

This pattern avoids manual counter updates and keeps predicate logic near the data iteration.

Match Any Condition, Not Just Equality

The condition can be any boolean expression. Keep simple predicates inline and move complex ones into helper functions.

python
1values = [3, 11, 14, 17, 20, 23, 28]
2
3even_indices = [i for i, v in enumerate(values) if v % 2 == 0]
4large_prime_like_indices = [i for i, v in enumerate(values) if v > 10 and v % 3 != 0]
5
6print(even_indices)
7print(large_prime_like_indices)

For clearer business rules:

python
1def is_high_priority(score: int) -> bool:
2    return score >= 90 or (score >= 80 and score % 5 == 0)
3
4scores = [71, 80, 83, 90, 95, 78]
5priority_positions = [i for i, s in enumerate(scores) if is_high_priority(s)]
6print(priority_positions)

Named predicates improve testability and code review quality.

First Match Without Building a Full List

If you only need the first index, use a generator and next. This avoids scanning extra items after the first hit.

python
1from typing import Iterable, Callable, TypeVar
2
3T = TypeVar("T")
4
5def first_index(items: Iterable[T], predicate: Callable[[T], bool], default: int = -1) -> int:
6    return next((i for i, item in enumerate(items) if predicate(item)), default)
7
8nums = [4, 8, 15, 16, 23, 42]
9print(first_index(nums, lambda x: x > 20))
10print(first_index(nums, lambda x: x < 0))

Using a default value keeps missing-case behavior explicit.

Nested Structures and Defensive Access

Real datasets often store dictionaries or objects rather than plain numbers. The same index pattern works, but guard against missing keys.

python
1events = [
2    {"id": "a1", "status": "ok", "latency_ms": 90},
3    {"id": "a2", "status": "error", "latency_ms": 140},
4    {"id": "a3", "status": "ok", "latency_ms": 210},
5    {"id": "a4", "latency_ms": 60},
6]
7
8problem_positions = [
9    i for i, e in enumerate(events)
10    if e.get("status") == "error" or e.get("latency_ms", 0) > 200
11]
12
13print(problem_positions)

Use dict.get when keys may be absent, otherwise one malformed row can stop the entire comprehension.

Alternatives for Very Large Numeric Data

For large numeric arrays, NumPy can be faster and more memory-efficient than Python loops.

python
1import numpy as np
2
3arr = np.array([12, 7, 5, 18, 7, 21, 7])
4idx = np.where(arr == 7)[0]
5print(idx.tolist())

Use this only when data is already in NumPy form or performance profiling justifies conversion cost.

Another practical option is itertools.compress when your condition is precomputed as a boolean mask.

python
1from itertools import compress
2
3items = [10, 11, 12, 13]
4mask = [False, True, True, False]
5positions = list(compress(range(len(items)), mask))
6print(positions)

Debugging and Testing Index Logic

Index-based bugs are often off-by-one mistakes or missing-case mistakes. Add tests that cover:

  • no matches
  • one match at first element
  • one match at last element
  • multiple matches
  • empty input list

A compact assertion set catches most regressions:

python
1assert [i for i, v in enumerate([1, 2, 3]) if v > 2] == [2]
2assert [i for i, v in enumerate([]) if v > 0] == []
3assert first_index([5, 6, 7], lambda x: x == 6) == 1
4assert first_index([5, 6, 7], lambda x: x == 9) == -1

Common Pitfalls

  • Using list.index when multiple matches are needed, which returns only the first occurrence.
  • Forgetting enumerate and trying to infer index from value, which fails with duplicates.
  • Calling next without a default, then getting StopIteration on no-match input.
  • Writing complex inline conditions that are hard to test or review.
  • Assuming all dictionary keys exist when filtering nested records.

Summary

  • Use list comprehension with enumerate for clear all-match index queries.
  • Use generator plus next for efficient first-match lookup.
  • Move complex predicates into named helper functions.
  • Handle nested data defensively with safe key access.
  • Switch to NumPy only when data size and profile results justify it.

Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design