Сортировка списка Dict по нескольким ключам, включая список

#python #sorting

#python #сортировка

Вопрос:

Я хотел бы отсортировать этот список dict по ключу списка, а затем по дате. Я пытаюсь отсортировать dicts по ‘label’ в соответствии с label_order, а затем по убыванию ‘date’.

 label_order = [3, 4, 2, 1]

data = [
    {'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)},
    {'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
    {'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
    {'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
    {'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
]
  

После сортировки будет выглядеть так:

 data = [
    {'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
    {'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
    {'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
    {'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
    {'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)},
]
  

Я пробовал лямбда-выражения и itemgetter, но мне трудно комбинировать правильные стратегии для ключа сортировки. Возможно, он просто пытается сделать слишком много за один раз.

Любая помощь или направление будут оценены.

Комментарии:

1. Подсказка: label_order.index(item['label']) возвращает индекс метки каждого элемента в этом списке, который соответствует порядку его сортировки.

2. Давайте посмотрим, какая лямбда-формула ближе всего к ожидаемому результату

3. Я бы изменил label_order на словарь, который сопоставляет метку с ее позицией сортировки. Т. е. сначала идет 3, затем 4, так и должно быть {3:1, 4:2, 2:3, 1:4} .

Ответ №1:

Более эффективный подход заключается в создании dict, который сопоставляет элементы в label_order с индексами, чтобы вы могли использовать индексы в качестве ключей при выполнении сортировки:

 keys = {n: i for i, n in enumerate(label_order)}
sorted(data, key=lambda d: (-keys[d['label']], d['date']), reverse=True)
  

Это возвращает:

 [{'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
 {'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
 {'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
 {'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
 {'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)}]
  

Ответ №2:

Немного сложно сортировать даты в обратном порядке. Вместо этого давайте используем отрицательное значение индекса метки, чтобы они были отсортированы в порядке убывания. Затем мы можем изменить сортировку и получить результаты в том порядке, в котором мы действительно хотим!

 from datetime import datetime

label_order = [3, 4, 2, 1]

data = [
    {'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)},
    {'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
    {'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
    {'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
    {'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
]

def descending_sort_key(item):
    return -label_order.index(item['label']), item['date']

data.sort(key=descending_sort_key, reverse=True)
  

Вуаля — нет математики даты или другого обмана.