Сортировка в два прохода
Что делать, когда одно правило по возрастанию, другое по убыванию, а минусом строку не развернёшь.
Задача
Отсортировать по двум правилам в разные стороны: категория по возрастанию, рейтинг по убыванию.
Для чисел это решается минусом в ключе:
sorted(tovary, key=lambda t: (t["категория"], -t["рейтинг"]))
А если развернуть надо строку? Минус к ней не применишь, а
reverse=True развернёт оба правила сразу.
Решение: два прохода
Сортировка в Python устойчива — она не переставляет равные элементы между собой. Значит можно отсортировать дважды:
# сначала СЛАБОЕ правило
shag1 = sorted(tovary, key=lambda t: t["название"], reverse=True)
# потом СИЛЬНОЕ
itog = sorted(shag1, key=lambda t: t["категория"])
Внутри одинаковых категорий сохранится порядок первого прохода — то есть названия с конца алфавита.
Порядок проходов обратный тому, как читается требование
Это главное место, где ошибаются. Требование звучит «сначала по категории, потом по названию», а сортировать надо наоборот: сначала по названию, потом по категории.
Перепутать легко, а результат при этом выглядит правдоподобно — отсортировано же. Поймать можно только на данных, где значения сильного правила совпадают.
Что такое устойчивость
pary = [("б", 1), ("а", 1), ("в", 0)]
sorted(pary, key=lambda p: p[1])
# [('в', 0), ('б', 1), ('а', 1)]
# ↑ «б» осталась перед «а», как и было
Не всякая сортировка устойчива: в C++ std::sort — нет, stable_sort
— да. В Python sorted и list.sort устойчивы всегда, и на это можно
полагаться, это записано в документации языка.