7

Let say I have :

student_tuples = [ ('john', 'A', 15),
                   ('peter', 'B', 12),
                   ('dave', 'C', 12)]

How do I sort it to be like this:

student_tuples = [('john', 'A', 15), ('dave', 'C', 12), ('peter', 'B', 12)]

What I can think is:

from operator import itemgetter

sorted(student_tuples, key=itemgetter(2,0), reverse=True)

but then the output will be:

student_tuples = [('john', 'A', 15), ('peter', 'B', 12), ('dave', 'C', 12)]

and that is not what I want. How can I do it using itemgetter or any other easier way?

Alexa Elis
  • 7,149
  • 6
  • 17
  • 11
  • Check this [post](http://stackoverflow.com/questions/3121979/how-to-sort-list-tuple-of-lists-tuples). I think there is everything you need. – ibi0tux May 14 '13 at 07:44
  • For what it's worth, I don't believe this is a duplicate. This question is specifically asking how to sort by one descending key and one ascending key, whereas the "duplicate" is only asking about multiple keys. – RichieHindle May 14 '13 at 16:48
  • 2
    No, it is instead a duplicate of [Python: List Sorting with Multiple Attributes and Mixed Order](http://stackoverflow.com/q/1516249) – Martijn Pieters May 14 '13 at 17:06
  • @RichieHindle "One [...] and one [...]" is grammatically equivalent to *multiple*; therefore, "Sort by one desc[...] and asc[...]" is the exact same as "[sorting by] multiple keys". – Jesse May 14 '13 at 17:07
  • Not sure if it is worth re-opening then closing again though. – Martijn Pieters May 14 '13 at 17:13

4 Answers4

11

This does it:

print sorted(student_tuples, key=lambda t: (-t[2], t[0]))
# [('john', 'A', 15), ('dave', 'C', 12), ('peter', 'B', 12)]
eyquem
  • 26,771
  • 7
  • 38
  • 46
RichieHindle
  • 272,464
  • 47
  • 358
  • 399
5

Write your own key-getting function.

student_tuples = [ ('john', 'A', 15), ('peter', 'B', 12), ('dave', 'C', 12)]

def student_key(args):
    name, letter, number = args
    return (-number, name)

>>> sorted(student_tuples, key=student_key)
[('john', 'A', 15), ('dave', 'C', 12), ('peter', 'B', 12)]
jamylak
  • 128,818
  • 30
  • 231
  • 230
Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
  • definitely the most readable way – jamylak May 14 '13 at 07:57
  • @Schoolboy Yes they are the same, saw his only when I posted my own. I wouldn't have posted it if I had seen his. Does SO have a policy about these situations? – Adrian Ratnapala May 14 '13 at 08:16
  • @Schoolboy No, it isn't exactly the same: in Adrian's solution, there's an unpacking, that means three assignements of objects to identifiers ``name,letter,number`` that aren't necessary, as the RichieHindle's solution shows. – eyquem May 14 '13 at 09:33
  • 1
    @eyquem That's what I said!! Let me rephrase my other comment... – pradyunsg May 14 '13 at 09:35
  • No, there's nothing like a "policy" that I know of.. But sometimes, (not here i guess) people just type the same thing as someone else, just for rep.. But here in your case, the difference is too little to say that you did so, and I don't think you did it either (copied the other answer) and that it is just a co-incidence.... :) – pradyunsg May 14 '13 at 09:35
  • @Schoolboy You wrote to me: "That's what I said!! Let me rephrase my other comment". No, you didn't said what I underlined. If you had said it, I wouldn't have written my comment. And if you had said it, why would Adrian have writen "Yes they are the same" ? – eyquem May 14 '13 at 14:37
  • What you said in your deleted comment was just that Adrian's and RichieHindle's answers were basically the same. Which is right in the principle, but not in the details. If you test the execution's times of these two solutions, you'll note that using ``(-t[2], t[0])`` instead of ``name, letter, number = args ; return (-number, name)`` is a little faster (comparison of times must be done correctly to see this). No one can verify things anymore since you deleted your comment. That's peculiar behavior. And I don't see the announced rephrasing of your deleted comment. – eyquem May 14 '13 at 14:37
4

Where you don't have a numeric field that can be easily negated, then you can always do a two-phase sort, relying on the fact that Python's .sort is stable:

student_tuples.sort(key=itemgetter(0))
student_tuples.sort(key=itemgetter(2), reverse=True)
Jon Clements
  • 138,671
  • 33
  • 247
  • 280
2

Nest the sorts. Most important/first priority sort in the outmost sort.

student_tuples = [ ('john', 'A', 15),
                   ('peter', 'B', 12),
                   ('dave', 'C', 12)]

from operator import itemgetter

sorted_ = sorted(sorted(student_tuples,key = itemgetter(0)), key=itemgetter(2), reverse=True)

gives

>>> sorted_
[('john', 'A', 15), ('dave', 'C', 12), ('peter', 'B', 12)]
kiriloff
  • 25,609
  • 37
  • 148
  • 229