Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort by multiple keys using different orderings [duplicate]

Possible Duplicate:
How to write Python sort key functions for descending values

In Python 3, it's pretty easy to sort a list of objects lexicographically using multiple keys. For example:

items.sort(key = lambda obj: obj.firstname, obj.lastname)

The reverse argument lets you specify whether you want ascending or descending order. But what do you do in the case where you want to sort by multiple keys, but you want to sort using descending order for the first key, and ascending order for the second?

For example, suppose we have an object with two attributes, points and name, where points is an int and name is a str. We want to sort a list of these objects by points in descending order (so that the object with the greatest number of points comes first), but for objects with an equal number of points, we want to sort these by name in alphabetical (ascending) order.

How can this be achieved?

like image 521
Channel72 Avatar asked Jul 13 '12 18:07

Channel72


3 Answers

There is no built-in way to handle this. For the general case, you must sort twice: first by the secondary sort, then by the primary sort. As @Mark Ransom mentioned in his comment, in many cases the variables are numeric, and so you can use the negative value to flip the ordering.

If you know the type of the variable you're trying to sort on and how to work with it, you could also write a key function that returns a decreasing value for increasing keys. See this thread for an example for strings. (Basically, you take the negative of the ASCII numerical value of the characters.)

In Python 2, you could also use a cmp function instead of a key, but this will likely make the sort slower. Whether it will make it too slow depends on how big and unsorted the list is. In Python 3, the cmp argument is gone, but as @Mark Ransom notes you could use cmp_to_key.

like image 68
BrenBarn Avatar answered Oct 24 '22 10:10

BrenBarn


items.sort(key = lambda obj: (obj.firstname, [(-ord(c) for c in obj.lastname)]))
like image 16
Marco de Wit Avatar answered Oct 24 '22 10:10

Marco de Wit


There is functools.cmp_to_key to convert a comparison function into a key compatible with the sorting functions. This was provided for sorts which used a comparison function in Python 2 and needed to be converted to Python 3 which no longer allows them.

Edit: There's also a suggestion in the Python wiki under the heading Sort Stability and Complex Sorts to do the sort in multiple passes, from least significant key to most significant. This works because Python's sort is guaranteed to be stable, so the previous order is maintained when equivalent keys are encountered.

like image 8
Mark Ransom Avatar answered Oct 24 '22 09:10

Mark Ransom