2016-01-14 18 views
9

Sto cercando di ordinare un elenco di tuple come questi:Ordina a vari livelli in Python

[('Pineapple', 1), ('Orange', 3), ('Banana', 1), ('Apple', 1), ('Cherry', 2)] 

L'elenco ordinato dovrebbe essere:

[('Orange', 3), ('Cherry', 2), ('Apple', 1), ('Banana', 1), ('Pineapple', 1)] 

Così, qui prima l'elenco deve essere ordinato basato su tuple[1] in ordine decrescente, quindi se i valori tuple() corrispondono come per Apple, Banana & Pineapple - l'elenco deve essere ulteriormente ordinato in base a tuple[0] in ascendi ordine.

Ho provato il possibile modi-

top_n.sort(key = operator.itemgetter(1, 0), reverse = True) 
# Output: [(Orange, 3), (Cherry, 2), (Pineapple, 1), (Banana, 1), (Apple, 1)] 

come "reverse = True", ananas, poi banana, ...

fine ho dovuto trovare una soluzione:

top_n.sort(key = operator.itemgetter(0), reverse = False) 
top_n.sort(key = operator.itemgetter(1), reverse = True) 

C'è un modo migliore per arrivare alla soluzione come il mio primo approccio. Sto cercando di esplorare di più su Python, cercando così un tale tipo di soluzione.

+0

In realtà si potrebbe semplicemente fare: 'top_n.sort(); top_n.sort (key = itemgetter (1), reverse = True) '. Perché "reverse = False" è l'impostazione predefinita. Anche l'uso di 'itemgetter (0)' non ha molto senso poiché le sequenze sono già ordinate dal primo elemento in modo da evitare semplicemente l'uso di 'chiave'. – Bakuriu

+0

@Bakuriu: Sì! Vero. Grazie per il suggerimento. –

risposta

2

Nel tuo caso, la soluzione Martijn Pieters è probabilmente la migliore, ma mi stava prendendo in considerazione quello che si potrebbe fare se si ha bisogno di fare questo per qualsiasi numero di parametri, e aveva bisogno di fare un po ' ascendente e discendente.

Questo approccio crea una funzione per generare un indice di ordinamento al volo. Chiamando getsortfunction con l'elenco di tuple da ordinare e una lista contenente gli indici e se devono essere in ordine inverso (ad esempio (2,True) significa secondo indice in ordine inverso), restituisce una funzione che crea l'indice di ordinamento per un oggetto. È piuttosto brutto, ma versatile.

def getsortfunction(values,indices): 
    sorts = [sorted(list(set(x[indices[i][0]] for x in values)),reverse=indices[i][1]) for i in range(len(indices))] 
    def sortfunction(y): 
     return tuple(sorts[i].index(y[indices[i][0]]) for i in range(len(indices))) 
    return sortfunction 

Esempi

a = [('Pineapple',1),('Orange',3),('Banana',1),('Apple',1),('Cherry',2)] 
# sort a by index 1 first (in reverse order) and then by index 0 in non-reverse order 
b = sorted(a,key=getsortfunction(a,[(1,True),(0,False)])) # gives desired list 

Con un ulteriore criterio

c = [('Pineapple',1,'Hawaii'),('Orange',3,'Florida'),('Banana',1,'Hawaii'),('Apple',1,'Washington'),('Cherry',2,'Washington')] 
# sort first by number (in reverse order) then by state, and finally by fruit 
d = sorted(c,key=getsortfunction(c,[(1,True),(2,False),(0,False)])) 

# sort c first by number (in reverse order), then by fruit, ignoring state 
e = sorted(c,key=getsortfunction(c,[(1,True),(0,False)])) 

Il getsortfunction prima crea un elenco annidata di valori univoci in ordine e restituisce una funzione che mappa ogni valore per essere ordinati a una tupla numerica che fornisce i suoi indici nell'elenco dei valori ordinati.

Il più grande vantaggio di questo è che è possibile determinare i criteri di ordinamento in fase di esecuzione (ad esempio, dalle richieste degli utenti).

+0

Questo è il tipo di soluzione che stavo cercando, ma ho pensato che forse sto esagerando (nuovo a Python) e l'ho interrotto. Anche se è un po 'brutto, credo che questa sia la soluzione efficace. Grazie! –

13

Fare in modo che la chiave restituisca una tupla del valore numerico negato e quindi la stringa. Negando, i numeri saranno ordinati in ordine decrescente, mentre le stringhe sono ordinati in ordine crescente:

top_n.sort(key=lambda t: (-t[1], t[0])) 

Sì, questo è un po 'di un hack, ma funziona ovunque è necessario ordinare in base a due criteri in contrario direzioni, e uno di questi criteri è numerico.

Demo:

>>> top_n = [('Pineapple', 1), ('Orange', 3), ('Banana', 1), ('Apple', 1), ('Cherry', 2)] 
>>> sorted(top_n, key=lambda t: (-t[1], t[0])) 
[('Orange', 3), ('Cherry', 2), ('Apple', 1), ('Banana', 1), ('Pineapple', 1)] 
+1

L'unico problema con questa soluzione è che la negazione funziona solo per i numeri interi. Non posso proporre niente di meglio, la mia soluzione era la stessa. – fodma1

+0

Cosa succede se entrambi gli argomenti delle tuple erano string? – Arman

+3

@Arman: ulteriore hacking: converte la stringa in una sequenza di interi negati ('[-chr (c) per c nella stringa]'). –