Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: How to perform a secondary descending alphabetic sort within a numeric primary sort

In Sorting a Python list by two criteria Fouad gave the following answer:

sorted(list, key=lambda x: (x[0], -x[1]))

I'd like to sort the following list primarily on the list of tuples primarily on the second item in each element in ascending order, followed by the first (alphabetic) item in descending order:

[('Ayoz', 1, 18, 7), ('Aidan', 2, 4, 9), ('Alan', 2, 4, 9), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)] 

to give the answer:

[('Ayoz', 1, 18, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9), ('Tariq', 5, 4, 2), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2)]

using the above approach if possible . I tried

tlist = [('Ayoz', 1, 18, 7), ('Aidan', 2, 4, 9), ('Alan', 2, 4, 9), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)]
sorted(tlist, key=lambda elem: (elem[1], -elem[0]))

but that only works when elem[0] is numeric (in this case it gives a TypeError: bad operand type for unary -: 'str')

I'll be grateful for any help. Python version is 3.4

like image 555
Disnami Avatar asked Feb 14 '16 00:02

Disnami


2 Answers

The built in sorting routines in Python are stable. That is, if two items have the same key value, then they keep the order they had relative to each other (the one closer to the front of the list stays closer to the front). So you can sort on multiple keys using multiple sorting passes.

from operator import itemgetter

tlist = [('Ayoz', 1, 18, 7), ('Aidan', 2, 4, 9), ('Alan', 2, 4, 9),
         ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)]

# sort by name in descending order
tlist.sort(key=itemgetter(0), reverse=True) 
print('pass 1:', tlist)

# sort by element 1 in ascending order.  If two items have the same value
# the names stay in the same order they had (descending order)
tlist.sort(key=itemgetter(1))
print(npass 2:', tlist)

Prints:

pass 1: [('Tariq', 5, 4, 2), ('Luke', 15, 16, 2), ('Ayoz', 1, 18, 7), ('Arlan', 5, 6, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9)]

pass 2: [('Ayoz', 1, 18, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9), ('Tariq', 5, 4, 2), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2)]
like image 96
RootTwo Avatar answered Oct 05 '22 07:10

RootTwo


tlist = [('Ayoz', 1, 18, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)] 

sorted(tlist, key=lambda elem: (elem[1],sorted(elem[0],reverse=True)))

Worked it out but it took me half an hour to type so I'm posting no matter what. I still welcome a better way of doing it.

like image 34
Disnami Avatar answered Oct 05 '22 08:10

Disnami