22

I have to sort a list with multiple attributes. I can do that in ascending order for ALL attributes easily with

L.sort(key=operator.attrgetter(attribute))....

but the problem is, that I have to use mixed configurations for ascending/descending... I have to "imitate" a bit the SQL Order By where you can do something like name ASC, year DESC. Is there a way to do this easily in Python without having to implement a custom compare function?

Georgy
  • 12,464
  • 7
  • 65
  • 73
Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148
  • Does this answer your question? [sort Python list with two keys but only one in reverse order](https://stackoverflow.com/questions/37693373/sort-python-list-with-two-keys-but-only-one-in-reverse-order) – user202729 Aug 14 '21 at 13:32

3 Answers3

33

If your attributes are numeric, you have this.

def mixed_order( a ):
    return ( a.attribute1, -a.attribute2 )

someList.sort( key=mixed_order )

If your attributes includes strings or other more complex objects, you have some choices.

The .sort() method is stable: you can do multiple passes. This is perhaps the simplest. It's also remarkably fast.

def key1( a ): return a.attribute1
def key2( a ): return a.attribute2

someList.sort( key=key2, reverse=True )
someList.sort( key=key1 )

If this is the only sort, you can define your own special-purpose comparison operators. Minimally, you need __eq__ and __lt__. The other four can be derived from these two by simple logic.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • thanks! callint sort() multiple times turned out to be the perfect solution for me! – Bernhard Vallant Oct 04 '09 at 16:45
  • Thanks for your answer! Just a bit of confusion regarding the first part; does returning a tuple perform a complex sort with higher index values taking lower precedence? I guess my question is more generally, how does `cmp` behave when handed two tuples? I looked around and can't find this documented. – Asad Saeeduddin Mar 13 '13 at 06:12
  • __eq__ can be derived from __lt__ using simple logic. :) – Tony Mar 30 '14 at 20:52
  • it might be helpful to point out that using this approach you have to reverse your sorting order (as you have done). – hardmooth Oct 14 '16 at 05:50
7

A custom function will render your code more readable. If you have many sorting operations and you don't want to create those functions though, you can use lambda's:

L.sort(lambda x, y: cmp(x.name, y.name) or -cmp(x.year, y.year))
RedGlyph
  • 11,309
  • 6
  • 37
  • 49
6

You can't, but writing the compare function is easy:

def my_cmp(a, b):
    return cmp(a.foo, b.foo) or cmp(b.bar, a.bar)
L.sort(my_cmp)
Lukáš Lalinský
  • 40,587
  • 6
  • 104
  • 126