Группировка коллекции
Группировка означает раскладывание элементов по корзинам. Для каждого элемента вы выбираете ключ, и все элементы с одинаковым ключом попадают в одну корзину.
Ключ вычисляется небольшим выражением, которое принимает один элемент и возвращает его ключ. Например, первую букву слова.
У списков в Python нет встроенного метода group_by. Естественный способ группировки — словарь, ключи которого являются ключами групп, а значения — списками элементов. Его заполняют циклом for и методом setdefault() или поручают collections.defaultdict(list) создавать пустые списки за вас.
Словарь хранит записи в порядке добавления, поэтому группы появляются в том порядке, в котором встретились их первые элементы. Вывод всегда выглядит одинаково.
Группировка по ключу
Самая простая группировка оставляет элементы как есть и только решает, в какую корзину попадёт каждый из них.
setdefault(key, []) возвращает список, хранящийся под ключом, а если ключа ещё нет, сначала сохраняет под ним новый пустой список. Поэтому groups.setdefault(key, []).append(w) работает даже для первого элемента группы. Элементы внутри группы сохраняют порядок, который они имели во входных данных.
Иногда элементы нужно изменить при группировке, например сохранить их в верхнем регистре. Вычислите ключ, вычислите новое значение и добавьте новое значение в группу. С defaultdict(list) достаточно простого groups[key].append(value).
Группировка слов по первой букве
Ключ — первая буква слова, w[0], переведённая в верхний регистр с помощью upper(). Слова, начинающиеся с одной и той же буквы, оказываются в одном списке.
Вывод: полная программа на Python
words = ["one", "two", "three", "four", "five"]
# setdefault возвращает список для ключа, при необходимости сначала сохраняя новый пустой
by_first_letter = {}
for w in words:
key = w[0].upper()
by_first_letter.setdefault(key, []).append(w)
print(by_first_letter)
Группировка с изменением значений
Ключ — первая буква в нижнем регистре. Сохраняемое значение — всё слово в верхнем регистре. defaultdict(list) начинает каждую новую группу с пустого списка; dict() превращает его обратно в обычный словарь для аккуратного вывода.
Вывод: полная программа на Python
from collections import defaultdict
words = ["one", "two", "three", "four", "five"]
# defaultdict сам создаёт пустой список для каждого нового ключа
groups = defaultdict(list)
for w in words:
key = w[0] # первая буква в нижнем регистре
value = w.upper() # значение, сохраняемое в группе
groups[key].append(value)
print(dict(groups))
Подсчёт в каждой группе
Часто элементы каждой группы не нужны — важно только, сколько их.
В стандартной библиотеке есть инструмент именно для этого: collections.Counter. Передайте ему ключи, и он вернёт похожий на словарь объект, сопоставляющий ключу количество. Чтение ключа, которого нет в Counter, даёт 0.
При выводе Counter показывает сначала самые большие количества.
Подсчёт слов по первой букве
Каждое слово превращается в свою первую букву, и Counter подсчитывает буквы. Буквы t и f встречаются по два раза, поэтому они выводятся перед o.
Вывод: полная программа на Python
from collections import Counter
words = ["one", "two", "three", "four", "five"]
# Counter сам подсчитывает каждый ключ; отсутствующий ключ считается как 0
counts = Counter(w[0] for w in words)
print(counts)
Свёртка и накопление по группам
Свернуть группу — значит шаг за шагом объединить её элементы в одно значение. Классический пример — нарастающая сумма: начать с 0 и прибавлять каждый элемент.
В Python нет fold() или aggregate() для групп. Используйте словарь, который хранит текущее значение каждой группы. Для каждого элемента вычислите его ключ и обновите значение, хранящееся под этим ключом.
Если у текущего значения есть начальное значение, например 0 для суммы, get(key, 0) даёт это начальное значение для отсутствующего ключа. Если значение должен начинать сам первый элемент, проверьте оператором in, существует ли уже ключ; подойдёт и get(key), который для отсутствующего ключа даёт None.
Сумма каждой группы
Каждое число помещается в группу "even" или "odd" и прибавляется к сумме этой группы. sums.get(key, 0) начинает новый ключ с 0, поэтому особый случай не нужен.
Вывод: полная программа на Python
numbers = [3, 4, 5, 6, 7, 8]
# get(key, 0) даёт 0 для новой группы, поэтому сложение сразу работает
sums = {}
for n in numbers:
key = "even" if n % 2 == 0 else "odd"
sums[key] = sums.get(key, 0) + n
print(sums)
Построение строки для каждой группы
Первое число группы начинает строку. Каждое следующее число присоединяется через "+". Проверка key not in chains различает эти два случая. str() превращает число в текст.
Вывод: полная программа на Python
numbers = [3, 4, 5, 6, 7, 8]
chains = {}
for n in numbers:
key = "even" if n % 2 == 0 else "odd"
# первое число группы начинает строку; следующие добавляются через "+"
if key not in chains:
chains[key] = str(n)
else:
chains[key] = chains[key] + "+" + str(n)
print(chains)