Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Advanced custom sort

I have a list of items that I would like to sort on multiple criterion.

Given input list:

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

Criterions:

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

The output should be:

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

I know this function needs to be passed onto the built-in sorted() but any ideas how to actually write it?

like image 975
Bartek R. Avatar asked Jan 14 '16 11:01

Bartek R.


People also ask

Why advanced sort is used in Excel?

Excel has Advanced Sorting options that can help you to perform multi-level sorting within a large database. Sometimes alphabetical or numerical Sorting do not suffice. In such cases, Advanced Sorting Options are required.

What is the difference between sort and custom sort?

A custom sort is one you define. For instance, you might need to sort T-shirts by small, medium, large and extra-large. Or you might sort temperatures by cold, warm and hot. A regular sort can't handle these types of requirements.


1 Answers

You could write a function for the key, returning a tuple with each portion of interest sorted by priority.

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',
]

Result:

>>> 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

When sorting tuples, the first elements are compared first. If they're the same, the tuples are sorted by their second elements, and so on. This is similar to the way ordinary words are sorted alphabetically.

Since we first want all the elements with 'I' together, then 'R', and so on, we'll put that first. To do that, we define a dictionary that gives each letter its desired priority. When we look up that letter (the fourth character in the string, s[3]) in that dictionary, there's the first part of the key.

Next, we want the number after that letter. For this, we'll use some short-circuiting to get either the fifth character and onward (s[4:]), or, if there aren't any, a 0. We send that to int, which will evaluate the number as a number to put '2' after '12' like it should be.

Finally, if the first two parts are the same, items will be sorted based on their first character. If this was a simpler sort we could just specify reverse=True. If this part was a number, we could just take its negative. We'll just turn that character into a number with ord() and then take the negative of that.

The result is keys of, for example, (0, 2, -65) for 'Aw I2'.

like image 85
TigerhawkT3 Avatar answered Oct 17 '22 18:10

TigerhawkT3