3

Assume we have a list of 5 tuple:

(a, b , c, d, e)

Let the list be student_tuples.

I wish to sort the list different orders for different fields.

The below mentioned command

sorted(student_tuples, key=itemgetter(2,4,0,1))

Will sort the list on ascending order for all the fields.

The below mentioned command

sorted(student_tuples, key=itemgetter(2,4,0,1), reverse=true)

Will sort the list on descending order for all the fields. What I am looking for is sorting a list on different orders for different fields. Is there a easy way to do so.

Based on the answers the technique could be used in any language

Thanks, Gudge

6
  • Do you mean to put the nth element of each tuple in a list of its own, then sort that list? Commented Aug 16, 2012 at 18:12
  • No this is not a home work problem. I can create separate lists and do it in a complicated manner. Commented Aug 16, 2012 at 18:15
  • What type of object are a,b,c,d,e ...? Commented Aug 16, 2012 at 18:17
  • Sorry, I did not get what you are asking. I have the list of 5 tuples. Each element in the list is 5 tuple. Commented Aug 16, 2012 at 18:18
  • But what are the elements inside the tuple? Commented Aug 16, 2012 at 18:18

2 Answers 2

9

If the values are numeric, you can do this easily using lambda:

sorted(student_tuples, key=lambda x: (x[2],x[4],-x[0],x[1]))
                                                #^ This field will be 
                                                #  in descending order 

If you can't easily negate the order inside a lambda function, you need to rely on the stableness of python sorting and sort a few times:

s = sorted(student_tuples, key=itemgetter(1))
s.sort(key=itemgetter(0),reversed=True)
s.sort(key=itemgetter(2,4))

I explain it in more depth in this answer.

Proof that my answers above accomplish the same thing (with numeric input):

import random
def rand_tuple():
    """ Return a random 5-tuple """
    return tuple( random.random() for _ in range(5) )

#100 random 5-tuples
lst = [ rand_tuple() for _ in range(100) ] 

#sort the list using method 1
sorted_lst = sorted(lst, key = lambda x: (x[2],x[4],-x[0],x[1])) 

#sort the list in place using method 2
lst.sort(key = itemgetter(1))  #<- Rightmost tuple element first!!!
lst.sort(key = itemgetter(0), reversed = True)
lst.sort(key = itemgetter(2,4))

print (lst == sorted_lst) #True -- Results are the same :-)
Sign up to request clarification or add additional context in comments.

12 Comments

sorted(student_tuples, key=lambda x: x[2],x[4],-x[0],x[1]) I get the following error message: SyntaxError: non-keyword arg after keyword arg
@gudge -- That's because I forgot the parenthesis. See the updated answer.
Thanks, It works. The second solution is more elegant and your explanation in the link is very good.
Hi, I don't think the solution works: x = [(100, 103), (100, 105), (98, 104)] Sort in ascending order for the first field: [(98, 104), (100, 103), (100, 105)] Sort in descending order on second field [(98, 104), (100, 105), (100, 103)]
@gudge -- It will only preserve the order of the first sort if the key function returns the same value in the subsequent sort. My two examples should be equivalent (notice how in the multi-sort context, I use the rightmost index (1) as the sort key first).
|
1

You could create a class with meaningful attribute names instead of just numeric indices; that would sort easily if you give it __cmp__ (python 2.x) or __eq__ and __lt__ plus @total_ordering (python 3.x).

Another option would be to keep the tuples, convert them to lists, and negate any numeric fields that you need to sort in reverse. You can kind of do this for strings, but it's not as neat as for numbers.

Part of the reason tuples sort fast, is that they aren't super flexible.

1 Comment

You might want to mention where total_ordering can be found ( i.e. functools.total_ordering)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.