I need to sort a list of objects. Currently I am doing it like this:
mylist = [aobject, bobject, cobject]
mylist.sort(key=mykey)
def mykey(sortelem):
attribute1 = sortelem.attribute1
attribute2 = sortelem.attribute2
return (attribute1, attribute2)
Now I want to sort by attribute1
ascending, but attribute2
descending. I can always just do something like this:
return (attribute1, -1 * attribute2)
But that seems very unpythonic. I have searched the web, but it is a very hard thing to search for.
CodePudding user response:
Using - attribute2
instead of attribute2
inside the tuple is the easiest way if attribute2
is a number.
If attribute2
is a string, or something else that cannot be multiplied by -1
in a meaningful way, then this trick doesn't work.
However, note that lst.sort
and sorted
are guaranteed to be stable sorts in python. In particular, this means that the two following code snippets are equivalent:
# SORT ACCORDING TO A TUPLE
l.sort(key=lambda x: (x[0], x[1]))
# SORT TWICE
l.sort(key=lambda x: x[1])
l.sort(key=lambda x: x[0])
Note how in the second version, we sorted by the tie-breaker first, then by the main criterion.
With the first version, we can sort in decreasing order of one of the two criterion by multiplying it by -1, if this criterion is numeric:
l.sort(key=lambda x: (x[0], -x[1]))
With the second version, however, we can sort in decreasing order of one of the two criterion, using optional argument reverse=True
, and this works for numbers as well as for other types, for instance strings.
l.sort(key=lambda x: x[1], reverse=True)
l.sort(key=lambda x: x[0])
Finally, note that instead of defining a custom function with def
or lambda
to use as the sorting key, you can use operator.itemgetter
or operator.attrgetter
.
For instance, the following two code snippets are equivalent:
# FIRST VERSION
l.sort(key=lambda x: (x[0], x[1]))
# SECOND VERSION
from operator import itemgetter
l.sort(key=itemgetter(0, 1))
The following three code snippets are equivalent:
# FIRST VERSION
def mykey(x):
x1 = x.attribute1
x2 = x.attribute2
return (x1, x2)
l.sort(key=mykey)
# SECOND VERSION
l.sort(key=lambda x: (x.attribute1, x.attribute2))
# THIRD VERSION
from operator import attrgetter
l.sort(key=attrgetter('attribute1', 'attribute2'))