54

If I have a list that contains a list that looks like this

['a',1] ['a',2] ['a',3] ['b',1] ['b',2] ['b',3]

How can I sort them so that element 0 is sorted descending and element 1 sorted ascending so the result would look like

['b',1] ['b',2] ['b',3] ['a',1] ['a',2] ['a',3]

Using itemgetter I can pass in reverse on element 0 but I then resort against element to of course it ruins the previous sort. I can't do a combined key since it needs to first sort descending and then ascending.

Georgy
  • 12,464
  • 7
  • 65
  • 73
Ominus
  • 5,501
  • 8
  • 40
  • 44

3 Answers3

68
L = [['a',1], ['a',2], ['a',3], ['b',1], ['b',2], ['b',3]]
L.sort(key=lambda k: (k[0], -k[1]), reverse=True)

L now contains:

[['b', 1], ['b', 2], ['b', 3], ['a', 1], ['a', 2], ['a', 3]]
Steven Rumbalski
  • 44,786
  • 9
  • 89
  • 119
  • Will accept as soon as it lets me. This is exactly what i needed. I am working on a bin location/warehouse routing issue for picklists in our warehouse and this did the trick. If you have the time could you explain what "lambda k: (k[0],-k[1])" does? Does the negative sign mean the reverse? Could i have gotten the same result with lambda k: (-k[0], k[1]) without the reverse argument? – Ominus Jul 12 '11 at 15:38
  • 3
    @Ominus: the `-k[1]` flips the sign on the integer which reverses their natural sorting order. You cannot do `-k[0]` as that value is a string. So, `reverse=True` reverses the sort order and `-k[1]` cancels out that reversal on the second element. – Steven Rumbalski Jul 12 '11 at 15:48
50

You can do successive rounds of sorting as python's sort is stable. You need to first sort on the secondary key though. See also the official HOW TO.

from operator import itemgetter
l = [['a',2], ['a',1], ['b', 2], ['a',3], ['b',1], ['b',3]]
l.sort(key=itemgetter(1))
l.sort(key=itemgetter(0), reverse=True)
# [['b', 1], ['b', 2], ['b', 3], ['a', 1], ['a', 2], ['a', 3]]
mhyfritz
  • 8,342
  • 2
  • 29
  • 29
  • 4
    This would be the way to go if the second sorting key doesn't have an opposite (like time, string...), as the accepted answer couldn't be used in that case. – Thierry Lathuille Sep 28 '18 at 10:36
4

Something like

def mycmp(a, b):

  res = cmp(a[0], b[0])
  if res == 0:
     return cmp(a[1], b[1])
  return res

newlist = sorted(input_list, cmp=mycmp)

The comparison method first checks the first item of each element. If they are equal it will check the second items of each element. The return value inside the mycmp() implementation may be negated in order to implemented a different sorting behavior.

Paulo Almeida
  • 7,803
  • 28
  • 36