2012-02-03 5 views
5

w Pythonie, mam coś jak poniżej (choć losowo tasuje):Jak sortować 2 Element krotką Strings w Mieszanego Zakonu Korzystanie parametr klucza (nie CMP)

l = [('a', 'x'), 
    ('a', 'y'), 
    ('a', 'z'), 
    ('b', 'x'), 
    ('b', 'y'), 
    ('b', 'z'), 
    ] 

Jeśli zadzwonię sorted(l), otrzymuję posortowany wynik (jak wyżej), czego można się spodziewać. Jednak potrzebuję przesłać dalej sortowanie na element pierwszego sortowania krotki i odwrotnego elementu drugi element. Innymi słowy, chciałbym następujący wynik:

l = [('a', 'z'), 
    ('a', 'y'), 
    ('a', 'x'), 
    ('b', 'z'), 
    ('b', 'y'), 
    ('b', 'x'), 
    ] 

w Python2.x, istnieje cmp parametr, który może być przekazany do sorted() osiągnąć ten wynik, ale Python3 nie ma już tego. Ma tylko parametr key. Czy istnieje sposób na osiągnięcie pożądanej kolejności sortowania przy użyciu tylko parametru key?

Wiem, że mogę zdefiniować zupełnie nową klasę do zawijania moich krotek, lub użyć czegoś w rodzaju functools.cmp_to_key (która również tworzy klasę opakowania), ale to wszystko wydaje się być bardzo trudne dla tak prostej operacji. Czy nie ma innego rozwiązania?

edytuj: Powinienem dodać, że wszystkie ciągi nie będą ciągami jednoznakowymi i że w niektórych przypadkach lista krotek zawiera dane inne niż łańcuchowe [tj. (ciąg bazowy, datetime)].

Odpowiedz

11

Sortowanie w Pythonie to stable od Python 2.2.

Tak, można sortować według drugiej wartości najpierw z odwrotnej flagą na:

>>> from operator import itemgetter 
>>> l.sort(key=itemgetter(1), reverse=True) 
>>> l 
[('a', 'z'), ('b', 'z'), ('a', 'y'), ('b', 'y'), ('a', 'x'), ('b', 'x')] 

Następnie można sortować według wartości pierwszy:

>>> l.sort(key=itemgetter(0)) 
>>> l 
[('a', 'z'), ('a', 'y'), ('a', 'x'), ('b', 'z'), ('b', 'y'), ('b', 'x')] 
2

W pojedynczy krok:

>>> l.sort(key=lambda t: (t[0], -ord(t[1]))) 
>>> l 
[('a', 'z'), ('a', 'y'), ('a', 'x'), ('b', 'z'), ('b', 'y'), ('b', 'x')] 

Za każdym razem, gdy trzeba posortować na wielu klawiszach, można sprawić, że kluczem będzie krotka, ponieważ krotki są porównywane leksykograficznie. Jeśli chcesz odwrócić sortowanie na jednym z kluczy, po prostu ustaw ten element jako ujemny. Oczywiście nie można po prostu uczynić łańcucha ujemnego, więc najpierw trzeba go przekonwertować na liczbę całkowitą z ord.

+3

Tylko w szkole są klucze sortowania szerokości 1 bajt. –

0
l = [('a', 'x'), 
('a', 'y'), 
('a', 'z'), 
('b', 'x'), 
('b', 'y'), 
('b', 'z'), 
] 

>>> sorted(l, key = lambda x: 2*abs(ord(x[0])-ord('a')) + abs(ord(x[1])-ord('z') 

Nadzieja to pomaga

+0

Tylko w szkole są klucze sortowania o szerokości 1 bajta. –

+0

@JohnMachin: czy mógłbyś to rozwinąć? Naprawdę nie wiem co masz na myśli – inspectorG4dget

+1

To była dziwaczna odpowiedź (i niekompletne pytanie z mojej strony), ale ord() działa tylko na pojedynczych znakach. Kod, nad którym pracuję, ma łańcuchy multicharacter, a także niektóre dane nie będące ciągami znaków. –

1

Python Sorting HOWTO zaleca, aby skorzystać z sort stability i zrobić coś w rodzaju w dwóch przejściach:

>>> l.sort(key=lambda t: t[1], reverse=True) # SECONDARY KEY: field 1 descending 
>>> l.sort(key=lambda t: t[0])     # PRIMARY KEY: field 0 ascending 
>>> l 
[('a', 'z'), ('a', 'y'), ('a', 'x'), ('b', 'z'), ('b', 'y'), ('b', 'x')] 
1

Można by to osiągnąć poprzez uruchomiony klasyfikowane dwukrotnie na liście, najpierw użyj sortowania odwrotnego z drugim elementem w każdej krotce, a następnie użyj sortowania normalnego na nowo posortowanej liście z pierwszym elementem w każdej krotce.

l = [('a', 'y'), 
    ('a', 'x'), 
    ('b', 'y'), 
    ('b', 'x'), 
    ('b', 'z'), 
    ('a', 'z'), 
    ] 
l = sorted(l, key=lambda tup: tup[1], reverse=True) 
l = sorted(l, key=lambda tup: tup[0]) 

Lub, jeśli wolisz:

l = sorted(sorted(l, key=lambda tup: tup[1], reverse=True), 
      key=lambda tup: tup[0])