2016-01-14 22 views
6

Ho un elenco di elementi che desidero ordinare su più criteri.Ordinamento personalizzato avanzato

lista di input Data:

cols = [ 
    'Aw H', 
    'Hm I1', 
    'Aw I2', 
    'Hm R', 
    'Aw R', 
    'Aw I1', 
    'Aw E', 
    'Hm I2', 
    'Hm H', 
    'Hm E', 
] 

criteri:

  • Hm> Aw
  • I> R> H> E

L'output dovrebbe essere:

cols = [ 
    'Hm I1', 
    'Aw I1', 
    'Hm I2', 
    'Aw I2', 
    'Hm R', 
    'Aw R', 
    'Hm H', 
    'Aw H', 
    'Hm E', 
    'Aw E' 
] 

So che questa funzione deve essere passata allo strumento integrato sorted() ma qualche idea su come effettivamente scriverlo?

+0

Possibile duplicato di http://stackoverflow.com/questions/14208256/sort-a-list-with-a-custom-order-in-python o http://stackoverflow.com/questions/3624323/python-how-to-custom-order-a-list – SIslam

risposta

7

si potrebbe scrivere una funzione per la chiave, restituendo un tuple con ciascuna porzione di interesse ordinata per priorità.

def k(s): 
    m = {'I':0, 'R':1, 'H':2, 'E':3} 
    return m[s[3]], int(s[4:] or 0), -ord(s[0]) 

cols = [ 
    'Aw H', 
    'Hm I1', 
    'Aw I2', 
    'Hm R', 
    'Aw R', 
    'Aw I1', 
    'Aw E', 
    'Hm I2', 
    'Hm H', 
    'Hm E', 
] 

Risultato:

>>> for i in sorted(cols, key=k): 
...  print(i) 
... 
Hm I1 
Aw I1 
Hm I2 
Aw I2 
Hm R 
Aw R 
Hm H 
Aw H 
Hm E 
Aw E 

Quando l'ordinamento tuple s, i primi elementi vengono confrontati prima. Se sono uguali, gli tuple vengono ordinati in base ai loro secondi elementi e così via. Questo è simile al modo in cui le parole ordinarie sono ordinate alfabeticamente.

Dal momento che prima vogliamo tutti gli elementi con 'I' insieme, quindi 'R' e così via, lo metteremo prima. Per fare ciò, definiamo un dizionario che assegna a ciascuna lettera la priorità desiderata. Quando cerchiamo quella lettera (il quarto carattere nella stringa, s[3]) in quel dizionario, c'è la prima parte della chiave.

Successivamente, vogliamo il numero dopo quella lettera. A tal fine, utilizzeremo alcuni cortocircuiti per ottenere il quinto carattere e in avanti (s[4:]) o, se non ce ne sono, uno 0. Lo inviamo a int, che valuterà il numero come numero da inserire '2' dopo '12' come dovrebbe essere.

Infine, se le prime due parti sono uguali, le voci verranno ordinate in base al loro primo carattere. Se questo fosse un tipo più semplice, potremmo semplicemente specificare reverse=True. Se questa parte fosse un numero, potremmo semplicemente prendere il suo negativo. Trasformeremo quel personaggio in un numero con ord() e poi prenderemo il negativo.

Il risultato è chiavi, ad esempio, per 'Aw I2'.

1

Quanto segue funziona:

cols = [ 
    'Aw H', 
    'Hm I1', 
    'Aw I2', 
    'Hm R', 
    'Aw R', 
    'Aw I1', 
    'Aw E', 
    'Hm I2', 
    'Hm H', 
    'Hm E', 
] 

def compare(x, y): 
    x1, x2 = x.split(' ') 
    y1, y2 = y.split(' ') 

    order = ['I1', 'I2', 'R', 'H', 'E'] 

    if order.index(x2) < order.index(y2): 
     return -1 
    elif order.index(x2) > order.index(y2): 
     return 1 
    else: 
     if x1 == 'Hm' and y1 == 'Aw': 
      return -1 
     elif x1 == 'Aw' and y1 == 'Hm': 
      return 1 
    return 0 

cols.sort(compare) 
print(cols) 

uscita

['Hm I1', 'Aw I1', 'Hm I2', 'Aw I2', 'Hm R', 'Aw R', 'Hm H', 'Aw H', 'Hm E', 'Aw E'] 
1

per il confronto Aw, Hm ed E, H, R, I, ecc è possibile definire due dizionari:

Ora dobbiamo analizzare ciascuna stringa in tre componenti: 1. (Aw o Hm) 2. (E o H o R o I) 3. Integer

Ora l'utilizzo di questi componenti ed i corrispondenti valori dal dizionario possiamo restituire una tupla dalla nostra funzione chiave:

>>> import re 
>>> from pprint import pprint 
>>> map_1 = {'Aw': 0, 'Hm': 1} 
>>> map_2 = {'E': 0, 'H': 1, 'R': 2, 'I': 3} 
>>> pattern = re.compile(r'(Aw|Hm)\s([EHRI])(\d*)') 
>>> def key_func(x): 
    a, b, c = pattern.search(x).groups() 
    return map_2[b], -int(c) if c else 0, map_1[a] 
... 
>>> pprint(sorted(cols, key=key_func, reverse=True)) 
['Hm I1', 
'Aw I1', 
'Hm I2', 
'Aw I2', 
'Hm R', 
'Aw R', 
'Hm H', 
'Aw H', 
'Hm E', 
'Aw E'] 
1

Creiamo qualcosa:

Come si assegnano i punti alle lettere?

def custom_sort(string): 
    value_dict = {'I':400 ,'R': 300,'H': 200,'E':100, 'Hm': 50, 'Aw':40} 
    s = string.split() 
    points = value_dict[s[0]] + value_dict[s[1][0]] 
    if -len(s[1])>1: 
     points -= int(s[1][1:]) 
    return -points 

cols = [ 
    'Aw H', 
    'Hm I1', 
    'Aw I2', 
    'Hm R', 
    'Aw R', 
    'Aw I1', 
    'Aw E', 
    'Hm I2', 
    'Hm H', 
    'Hm E', 
] 

print sorted(cols, key=custom_sort) 
Problemi correlati