


itertools.groupby объединяет соседние элементы с одинаковым ключом. Если ключ встретился, затем сменился и появился снова, получится новая группа. Для объединения всех записей одного ключа данные обычно сначала сортируют по той же функции.
На Mentoring.digital рассмотрим учебные записи заказов. Главный вопрос здесь не в краткости кода, а в том, должны ли объединяться все одинаковые статусы или только последовательные участки.
from itertools import groupby
from operator import itemgetter
rows = [
{"id": 1, "status": "new"},
{"id": 2, "status": "done"},
{"id": 3, "status": "new"},
]
key = itemgetter("status")
groups = [(status, [row["id"] for row in part])
for status, part in groupby(rows, key)]
print(groups)
# [('new', [1]), ('done', [2]), ('new', [3])]
Такой результат правильный. Он полезен, например, при выделении непрерывных серий событий. Сортировка в этом случае могла бы уничтожить смысл последовательности.
ordered = sorted(rows, key=key)
groups = [(status, [row["id"] for row in part])
for status, part in groupby(ordered, key)]
print(groups)
# [('done', [2]), ('new', [1, 3])]
У сортировки и группировки одна функция key. Если отсортировать по идентификатору, а группировать по статусу, одинаковые статусы не обязательно окажутся рядом. sorted создаёт новый список; исходный rows в этом примере сохраняет порядок.
Нужно заранее решить, как обрабатывать отсутствующий status. itemgetter при отсутствии ключа выдаст KeyError. Не стоит молча заменять отсутствие на «неизвестно», если по требованиям это ошибка входных данных. Сначала определите правило, потом выражайте его функцией ключа.
Второй элемент пары из groupby тоже является итератором. Группы используют общий источник. Когда внешний итератор проходит дальше, предыдущая группа может оказаться уже недоступной для чтения. Если данные потребуются после цикла, материализуйте их внутри него.
saved = []
for status, part in groupby(ordered, key):
saved.append((status, list(part)))
Сохранение списков требует памяти на их содержимое. Если нужен только подсчёт, можно обработать группу сразу, например через sum(1 for _ in part), и сохранить число. Если вход уже отсортирован подходящим образом, повторная сортировка не нужна.
Если требуется собрать все записи по ключу без сортировки, рассмотрите словарь списков. Он накапливает данные, а не выделяет соседние серии.
from collections import defaultdict
by_status = defaultdict(list)
for row in rows:
by_status[key(row)].append(row)
В учебном наборе new получит записи 1 и 3, done получит запись 2. Проверьте пустой вход, один элемент, чередующиеся ключи и отсутствующее поле. Эти случаи помогают отличить ошибку реализации от неверно выбранной операции.
Практику с итераторами дают курсы программирования на Python. Разобрать обработку вашего набора данных можно с ментором по Python.