2013-05-02 3 views
1

Надеемся, что это будет иметь смысл ...Сортировка списка кортежей с двумя входами кортежей в обратном порядке

У меня есть список кортежей следующего вида:

list_of_tuples = [('a', 1), ('b', 3), ('b', 2), ('a', 3)] 

Нужный выход является

sorted_list_of_tuples = [('a', 1), ('b', 2), ('b', 3), ('a', 3)] 

Проблема заключается в том, что я хочу, чтобы вторая запись увеличивалась, а первая запись уменьшалась.

import operator as op  
sorted_list_of_tuples = sorted(list_of_tuples, key=op.itemgetter(2, 0)) 

Это, конечно же, сортирует оба поля для увеличения. Я не могу придумать милый (т. Е. Пару строк) способ сделать это. У кого-нибудь есть способ легко справиться с подобной сортировкой?

Я, кажется, помню, что вы можете ссылаться на элементы понимания списка внутри скобок, используя _, так что, возможно, это место для начала?


Возможно, я не был ясен: целое в этом случае более важно. Его порядок должен увеличиваться по всему списку. Когда есть галстук (т. Е. Вторая позиция равна), я хочу, чтобы «b» произошло до «a».

+0

* Кажется, я помню, что вы можете ссылаться на элементы понимания списка внутри скобок, используя _, поэтому возможно, это место для начала? * - Я не уверен, что вы имеете в виду, но '_' ничего особенного в Python, просто имя переменной. –

+0

@ Lattyware: Я понимаю. То, что я вспомнил, было в этой теме: http://stackoverflow.com/questions/101268/hidden-features-of-python См. Запись под названием «Ссылка на понимание списка по мере его создания ...» – BenDundee

+0

Как уже отмечалось, , это неясная деталь реализации, а не то, что нужно использовать в целом. Здесь также не очень важно. –

ответ

3

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

Я хочу, чтобы вторая запись увеличивалась, а первая запись уменьшалась.

Итак, ключ:

def make_key(my_tuple): 
    return my_tuple[1], -my_tuple[0] 

За исключением, конечно, что - не работает таким образом на строки, так что вам нужно что-то любитель.

Или, может быть, не ... в то время как первый элемент каждого кортежа является строкой, то вторая представляет собой целое число, так что мы можем просто отрицать ключевую функцию, и использовать reverse ООН-отрицать это:

def make_key(my_tuple): 
    return -my_tuple[1], my_tuple[0] 

sorted_list_of_tuples = sorted(list_of_tuples, key=make_key, reverse=True) 

Если вы хотите, чтобы сэкономить несколько нажатий клавиш:

sorted_list_of_tuples = sorted(list_of_tuples, 
           key=lambda x: (x[1], x[0]), reverse=True) 

Это не единственный трюк, который бы работать здесь.Например, поскольку все ваши строки являются 1-символьными строками, ord(x) < ord(y) iff x < y.

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

def compare_my_tuples(lhs, rhs):   
    if rhs[1] > lhs[0]: return 1 
    elif rhs[1] < lhs[0]: return -1 
    elif rhs[0] > lhs[0]: return -1 
    elif rhs[0] < rhs[0]: return 1 
    else: return 0 

sorted_list_of_tuples = sorted(list_of_tuples, 
           key=functools.cmp_to_key(compare_my_tuples)) 

Или, конечно, вы можете разбить его на два вида, как и в ответ steveha в. (Да, это может занять в два раза больше ... но в большинстве приложений это не будет иметь никакого значения.)

2

Конечно. Встроенный сорт Python является «стабильным». Итак, выберите, какой вид вы хотите быть более важным, и сделайте это второй. Сделайте менее важный вид, а затем выполните сортировку по более важным критериям.

Рабочий код:

import operator as op 

list_of_tuples = [('a', 1), ('b', 3), ('b', 2), ('a', 3)] 

list_of_tuples.sort(key=op.itemgetter(0), reverse=True) 
list_of_tuples.sort(key=op.itemgetter(1)) 

assert list_of_tuples == [('a', 1), ('b', 2), ('b', 3), ('a', 3)] 

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

def custom_key_fn(tup): 
    ch, n = tup # unpack tuple 
    return (n, -ord(ch)) 

list_of_tuples = [('a', 1), ('b', 3), ('b', 2), ('a', 3)] 
list_of_tuples.sort(key=custom_key_fn) 

assert list_of_tuples == [('a', 1), ('b', 2), ('b', 3), ('a', 3)] 
+0

Вы не можете сделать это с помощью одного вида? – BenDundee

+0

@BenDundee: Уверен, вы можете, это просто требует более сложной ключевой функции. Это компромисс. – abarnert

+0

Я уже писал это, когда вы спрашивали. :-) – steveha

Смежные вопросы