How to Split a List by a Predicate into Two Lists in Python

Partition any Python list into two lists based on a predicate: items that match go into one list, everything else into the other.

Easy Python 3.9+ Aug 9, 2026 Algorithms & data structures 11 views 0 copies

Python code

20 lines
Python 3.9+
from typing import Callable, List, TypeVar

T = TypeVar("T")

def split_by_predicate(items: List[T], predicate: Callable[[T], bool]) -> tuple[List[T], List[T]]:
    matching = []
    non_matching = []
    for item in items:
        if predicate(item):
            matching.append(item)
        else:
            non_matching.append(item)
    return matching, non_matching


if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    evens, odds = split_by_predicate(numbers, lambda n: n % 2 == 0)
    print(f"Evens: {evens}")
    print(f"Odds: {odds}")

Output

stdout
Evens: [2, 4, 6, 8, 10]
Odds: [1, 3, 5, 7, 9]

How it works

This function iterates over the input list once and appends each item to either the matching or non_matching list depending on whether predicate(item) returns True. Because it builds two new lists, the original ordering is preserved in each result. The TypeVar and Callable types make the function generic and reusable for any element type. This approach runs in O(n) time with O(n) extra memory for the two output lists.

Common mistakes

  • Forgetting to handle the empty list case — the function returns two empty lists, which is usually fine but can surprise callers.
  • Mutating the input list inside the loop, which can cause skipped items or infinite loops.
  • Returning `(non_matching, matching)` by accident, swapping the order of the output lists.
  • Using `filter` and list comprehension separately for each side, leading to two passes instead of one.

Variations

  1. Use `filter(predicate, items)` and `filter(lambda x: not predicate(x), items)` for a functional one-liner.
  2. Use `[x for x in items if predicate(x)]` and `[x for x in items if not predicate(x)]` for readability at the cost of double iteration.

Real-world use cases

  • Splitting user records into active vs inactive before sending batch email notifications.
  • Partitioning log messages into error vs non-error for separate alerting pipelines.
  • Categorizing inventory items into in-stock vs backorder at the start of a replenishment job.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Algorithms & data structures

Related tutorials and quizzes for this topic.