Home > Enterprise >  Reverse sort order for part of a key
Reverse sort order for part of a key

Time:04-30

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'))
  • Related