7

Сортировка списка по нескольким атрибутам?

2

У меня есть список списков:

[[12, 'tall', 'blue', 1],
 [2, 'short', 'red', 9],
 [4, 'tall', 'blue', 13]]

Если я хочу отсортировать его по одному элементу, например, по элементу "tall/short", я могу сделать это с помощью функции s = sorted(s, key=itemgetter(1)).

Однако если я хочу отсортировать по двум критериям - сначала по "tall/short", а затем по цвету, я мог бы отсортировать его дважды, по каждому элементу по отдельности. Но есть ли более быстрый способ сделать это?

5 ответ(ов)

12

Ключом для сортировки может быть функция, возвращающая кортеж:

s = sorted(s, key=lambda x: (x[1], x[2]))

Однако вы можете добиться того же результата с помощью itemgetter, что будет быстрее и избавит от вызова функции на Python:

import operator
s = sorted(s, key=operator.itemgetter(1, 2))

Также обратите внимание, что в данном случае вы можете использовать метод sort вместо sorted, чтобы избежать повторного присваивания:

s.sort(key=operator.itemgetter(1, 2))
0

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

Вместо того чтобы использовать два вызова функции sorted, можно применить один вызов с кортежем в качестве ключа сортировки. Это позволит вам задать оба условия сортировки в одном месте, что сделает код более читаемым. Вот как это может выглядеть:

a = [('Al', 2), ('Bill', 1), ('Carol', 2), ('Abel', 3), ('Zeke', 2), ('Chris', 1)]
b = sorted(a, key=lambda x: (-x[1], x[0]))
print(b)  

В этом примере мы используем кортеж (-x[1], x[0]) в качестве ключа для сортировки. Знак минус перед x[1] обеспечивает сортировку по убыванию для целочисленных значений, а x[0] сортирует строки в алфавитном порядке.

Результат выполнения этого кода будет прежним:

[('Abel', 3), ('Al', 2), ('Carol', 2), ('Zeke', 2), ('Bill', 1), ('Chris', 1)]

Таким образом, этот метод более питоновый и, вероятно, будет более понятен другим разработчикам.

0

Несколько лет спустя, но я хочу сортировать по двум критериям и использовать reverse=True. Если вдруг кому-то это будет интересно, вы можете обернуть свои критерии (функции) в скобки:

s = sorted(my_list, key=lambda i: (criteria_1(i), criteria_2(i)), reverse=True)
0

Похоже, вместо tuple вы можете использовать list. Это становится особенно важным, когда вы работаете с атрибутами, а не с "магическими индексами" списка или кортежа.

В моем случае мне нужно было сортировать по нескольким атрибутам класса, где входные ключи были строками. Я хотел иметь разную сортировку в разных местах и общую сортировку по умолчанию для родительского класса, с которым взаимодействовали клиенты; при этом мне нужно было переопределять "ключи сортировки", только когда это действительно необходимо, но также так, чтобы я мог хранить их в виде списков, которыми мог бы делиться класс.

Сначала я определил вспомогательный метод:

def attr_sort(self, attrs=['someAttributeString']):
    '''вспомогательный метод для сортировки по атрибутам, названным строками в attrs в указанном порядке'''
    return lambda k: [getattr(k, attr) for attr in attrs]

Затем, чтобы использовать его:

# будет определено в другом месте, но показываю здесь для краткости
self.SortListA = ['attrA', 'attrB']
self.SortListB = ['attrC', 'attrA']
records = .... # список моих объектов для сортировки
records.sort(key=self.attr_sort(attrs=self.SortListA))
# возможно, позже, рядом или в другой функции
more_records = .... # другой список
more_records.sort(key=self.attr_sort(attrs=self.SortListB))

Это позволит вам использовать сгенерированную лямбда-функцию для сортировки списка по object.attrA, затем по object.attrB, если object имеет соответствующий геттер для предоставленных строковых имен. Во втором случае сортировка будет выполнена по object.attrC, затем по object.attrA.

Такой подход также позволяет потенциально предоставить возможность выбора параметров сортировки, которые могут быть разделены потребителем, юнит-тестом или, возможно, позволяет им указать, как они хотят, чтобы сортировка была выполнена для какой-то операции в вашем API, предоставив лишь список, а не связывая их с реализацией вашего бекенда.

0

Чтобы преобразовать список списков в список кортежей и отсортировать кортежи по нескольким полям, вы можете использовать такой подход:

data = [[12, 'tall', 'blue', 1], [2, 'short', 'red', 9], [4, 'tall', 'blue', 13]]

# Преобразуем каждый вложенный список в кортеж
data = [tuple(x) for x in data]

# Сортируем кортежи по нескольким полям: сначала по второму элементу (рост), затем по третьему (цвет)
result = sorted(data, key=lambda x: (x[1], x[2]))

print(result)

Выходной результат будет следующим:

[(2, 'short', 'red', 9), (12, 'tall', 'blue', 1), (4, 'tall', 'blue', 13)]

Таким образом, вы сначала комбинируете элементы из вложенных списков в кортежи, а затем сортируете их по указанным полям с помощью функции sorted и лямбда-функции для задания ключа сортировки.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь