itertools.groupby: Group Sorted Data Correctly

Published on: August 30, 2026
Reading time: 2 minutes
A developer typing code on a laptop with a Python book beside in an office.

itertools.groupby() groups consecutive elements that produce the same key. It is lazy, consumes input only as needed, and can process large ordered streams without loading everything into memory. Its behavior is different from SQL GROUP BY: equal keys that are not adjacent create separate groups.

This guide covers sorting, shared group iterators, streaming aggregation, object attributes, nested grouping, and common mistakes.

Basic example

from itertools import groupby

values = [1, 1, 2, 2, 2, 3]
for key, group in groupby(values):
    print(key, list(group))

Each run of equal consecutive values becomes one group.

Adjacency is the core rule

values = [1, 2, 1]
print([(key, list(group)) for key, group in groupby(values)])

The value 1 appears twice because the matching values are separated. Sort first when all equal keys must be combined.

Sort with the same key

records.sort(key=lambda item: item["category"])
for category, group in groupby(records, key=lambda item: item["category"]):
    items = list(group)
    process(category, items)

The sorting key and grouping key should represent the same ordering rule.

Groups share the source iterator

Each group is a subiterator connected to the main iterator. Advancing to the next key invalidates the previous group. Materialize a group immediately if it must be retained.

for key, group in groupby(data, key=get_key):
    items = list(group)
    store(key, items)

Streaming aggregation

for category, group in groupby(records, key=lambda row: row.category):
    total = sum(row.amount for row in group)
    print(category, total)

The group is consumed directly by sum, keeping memory usage low.

Counting without a list

for key, group in groupby(data, key=get_key):
    count = sum(1 for _ in group)
    print(key, count)

Grouping objects

from operator import attrgetter

orders.sort(key=attrgetter("customer_id"))
for customer_id, group in groupby(orders, key=attrgetter("customer_id")):
    process_orders(customer_id, group)

attrgetter can make a repeated attribute key clearer than multiple lambdas.

Best use case: already ordered input

Database results with ORDER BY, sorted files, and monotonic event streams are excellent inputs. groupby can aggregate them incrementally.

Dictionary versus groupby

A dictionary of lists groups keys regardless of order but retains every item. groupby uses little memory but requires matching keys to be adjacent.

Text normalization

names.sort(key=str.casefold)
for key, group in groupby(names, key=str.casefold):
    print(key, list(group))

Use the same normalization for sorting and grouping. Human-language sorting may require locale-aware rules.

Nested grouping

rows.sort(key=lambda row: (row.country, row.city))
for country, country_group in groupby(rows, key=lambda row: row.country):
    country_rows = list(country_group)
    for city, city_group in groupby(country_rows, key=lambda row: row.city):
        process(country, city, city_group)

The outer group is materialized because all subgroups share the underlying iterator.

Common mistakes

  • Expecting nonadjacent equal keys to merge.
  • Sorting and grouping with incompatible keys.
  • Saving a group iterator for later consumption.
  • Materializing every group when streaming would work.
  • Using a key function with side effects.

Document ordering requirements, keep key functions pure, consume each group before advancing, and choose materialization only when retention is needed. See the internal guides to Python itertools and itertools.pairwise.

Conclusion

itertools.groupby is ideal for segmenting sorted sequences and building low-memory aggregations. Understanding adjacency and shared iterators is essential for correct results.

External sources

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Detailed close-up of yellow and white albino python scales, capturing texture and pattern.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    itertools.accumulate: Running Sums and State

    Learn Python itertools.accumulate for running sums, balances, records, custom state transitions, and lazy data pipelines.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    contextlib.chdir: Risks of Changing Directories

    Learn Python contextlib.chdir, its global-state and concurrency risks, and when pathlib or subprocess cwd is the safer design.

    Ler mais

    Tempo de leitura: 3 minutos
    30/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python nullcontext: Optional Contexts

    Use Python nullcontext to unify optional files, locks, transactions, sessions, and borrowed resources without duplicate branches.

    Ler mais

    Tempo de leitura: 4 minutos
    30/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    contextlib.aclosing: Close Async Generators Safely

    Learn Python contextlib.aclosing to close async generators safely after break, return, exceptions, cancellation, and partial consumption.

    Ler mais

    Tempo de leitura: 5 minutos
    30/08/2026
    Detailed close-up texture of a snake's patterned skin showcasing natural patterns and scales.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    weakref.finalize: Cleanup Without Retaining Objects

    Learn Python weakref.finalize for safe fallback cleanup without retaining objects, including alive, detach, shutdown, and explicit close.

    Ler mais

    Tempo de leitura: 5 minutos
    30/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    SimpleNamespace: Lightweight Attribute Objects

    Learn Python SimpleNamespace for lightweight attribute objects, dictionary conversion, copying, JSON, and choosing better typed models.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026