I have a list like this:
list_results=[('Horror', 2), ('Romance', 2), ('Comedy', 2), ('History', 2), ('Adventure', 1), ('Action', 3)]
I wish to sort the number in descending order and if numbers were the same, according to the name in ascending order.
I tried the following code:
sortlist=sorted(list_results,key=lambda x:(x[1],x[0]))
and the reverse but I couldn't figure out to do it.
The answer that I'm looking for is:
[('Action', 3), ('Comedy', 2) ,('History', 2),('Horror', 2), ('Romance', 2), ('Adventure', 1), ]
CodePudding user response:
First sort the list by the first item, then by the second item:
list_results = sorted(list_results, key=lambda x:x[0])
list_results = sorted(list_results, key=lambda x:x[1], reverse=True)
or better yet without copying:
import operator
list_results.sort(key=operator.itemgetter(0))
list_results.sort(key=operator.itemgetter(1), reverse=True)
Python's sort algorithm is Timsort. It is a stable algorithm meaning if 2 values are the same, they'll stay in their original order.
If you sort alphabetically first, and then by the priority, the list will be sorted according to the alphabet, then re-sorted according to priority with alphabet being secondary.
CodePudding user response:
You want to sort according to two criteria, with one criterion acting as a tie-breaker for the other. Since python's sorted
and list.sort
are guaranteed to be stable sorts, one solution is to sort the list twice: first sort it by the tie-breaker, then sort it by the main criterion. This is @Bharel's answer.
Another possibility is to sort only once, using a tuple as the key. Python's sorted
and list.sort
both offer a reverse= True or False
argument to specify a sort in increasing or decreasing order; but in your case, we want to sort in decreasing order with respect to the first criterion, and increasing order with respect to the second criterion. The reverse
keyword is not helpful because it is all-or-nothing: it will not allow us to choose which criterion to reverse.
Since the first criterion is numeric (an integer), a simple trick to sort in reverse order is to negate it with a minus sign:
sortlist = sorted(list_results, key=lambda x:(-x[1], x[0]))
Note the -x[1]
instead of just x[1]
.
Here are two arguments in favour of sorting once by a tuple, rather than twice:
- When sorting according to
(-x[1], x[0])
, it is immediately clear that-x[1]
is the main criterion, andx[0]
is only a tie-breaker. By contrast, if you sort twice, someone reading your code needs to take a second to understand that the last sort is the most important, and the previous sort is only there as a tie-breaker relying onsorted
being a stable sort. - If the list is long, sorting once with a tuple key is probably faster than sorting twice with simple keys. This is especially true because the second key is a string; comparing string is slower than comparing integers. If you use tuples, the strings will only be compared for two items who are ex aequo on the first key; but if you sort twice, about
n log(n)
string comparisons will be performed in the first sort.
If your list is small, it probably doesn't matter which version is faster (unless you're repeatedly sorting lots of small lists...), so it's a matter of preference and readability.