Python
List Partitioning
Data Structures
Conditional Logic
Programming Tips

How can I partition split up, divide a list based on a condition?

Master System Design with Codemia

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

Introduction

Partitioning a list means dividing its elements into groups according to a predicate. In Python, the most common case is a two-way split such as "items that satisfy the condition" and "items that do not," but the implementation details still matter if you care about order, memory use, or single-pass processing.

The Clearest Two-List Approach

If the list is not enormous, two list comprehensions are often the easiest solution to read.

python
1numbers = [1, 2, 3, 4, 5, 6]
2
3evens = [n for n in numbers if n % 2 == 0]
4odds = [n for n in numbers if n % 2 != 0]
5
6print(evens)
7print(odds)

This keeps the original order within each group and is perfectly fine for many real programs.

One Pass Is Better When Work Matters

The two-comprehension version evaluates the condition twice for every element. If the predicate is expensive or the iterable is large, a single-pass partition is more efficient.

python
1from typing import Callable, Iterable, TypeVar
2
3T = TypeVar("T")
4
5
6def partition(items: Iterable[T], predicate: Callable[[T], bool]):
7    matches = []
8    non_matches = []
9    for item in items:
10        if predicate(item):
11            matches.append(item)
12        else:
13            non_matches.append(item)
14    return matches, non_matches
15
16
17numbers = [1, 2, 3, 4, 5, 6]
18evens, odds = partition(numbers, lambda n: n % 2 == 0)
19print(evens)
20print(odds)

This reads the input once and still preserves order.

Partitioning Without Losing Order

People sometimes reach for sorting or itertools.groupby, but those tools solve a different problem. groupby only groups consecutive items with the same key, so it does not automatically partition an unsorted list into all true values and all false values.

python
1from itertools import groupby
2
3values = [1, 2, 3, 4, 5]
4for key, group in groupby(values, key=lambda n: n % 2 == 0):
5    print(key, list(group))

This produces alternating groups as the predicate result changes. It is useful for runs of similar items, not for a full true-versus-false split.

Partitioning Into More Than Two Buckets

If you need more than two groups, build buckets by key.

python
1from collections import defaultdict
2
3words = ["apple", "ant", "banana", "boat", "cat"]
4buckets = defaultdict(list)
5
6for word in words:
7    buckets[word[0]].append(word)
8
9print(dict(buckets))

That is not just partitioning by a boolean condition anymore; it is grouping by a derived key. The idea is related, but the data structure changes from a pair of lists to a mapping of buckets.

Generator Inputs And Consumption

If the input is an iterator rather than a concrete list, remember that once you consume it, it is gone.

python
1def values():
2    for n in range(5):
3        yield n
4
5small, large = partition(values(), lambda n: n < 3)
6print(small)
7print(large)

This works because the partition function reads the generator once and stores the results. Using two separate comprehensions over the same generator would not behave the same way because the first pass would exhaust it.

In-Place Partitioning Is A Different Problem

Sometimes people mean the in-place partition step used in quicksort, where elements are rearranged inside the same array. That is a different requirement from creating two new Python lists while preserving order. In-place partitioning can save memory, but it usually changes element order and has more implementation complexity.

For everyday Python code, returning two new lists is usually the most pragmatic answer.

Common Pitfalls

The most common mistake is using groupby and expecting it to collect all matching values across the entire list without prior sorting. Another is forgetting that two list comprehensions evaluate the condition twice and may not be suitable for expensive predicates. Developers also sometimes partition a generator twice and then wonder why the second result is empty. Finally, an in-place algorithm is not a drop-in replacement if the caller depends on stable relative order.

Summary

  • Two list comprehensions are the simplest partitioning technique for ordinary lists.
  • A single-pass helper is better when the predicate is expensive or the input is an iterator.
  • 'itertools.groupby groups consecutive runs, not all matching values globally.'
  • Grouping into many buckets is a related but different problem from boolean partitioning.
  • Decide early whether you need stable order, one-pass consumption, or in-place rearrangement.

Course illustration
Course illustration