I am trying to sort a list of tuples like these:
[('Pineapple', 1), ('Orange', 3), ('Banana', 1), ('Apple', 1), ('Cherry', 2)]
The sorted list should be:
[('Orange', 3), ('Cherry', 2), ('Apple', 1), ('Banana', 1), ('Pineapple', 1)]
So, here 1st the list should be sorted based on
tuple in descending order, then if the
tuple values (
tuple) match like for
Pineapple - list should be further sorted based on
tuple in ascending order.
I have tried the possible ways-
top_n.sort(key = operator.itemgetter(1, 0), reverse = True)
# Output: [(Orange, 3), (Cherry, 2), (Pineapple, 1), (Banana, 1), (Apple, 1)]
"reverse = True", Pineapple, then Banana,...
I finally had to come up with a solution:
top_n.sort(key = operator.itemgetter(0), reverse = False)
top_n.sort(key = operator.itemgetter(1), reverse = True)
Is there any better way to get to the solution like my 1st approach. I am trying to explore more about Python, thus seeking such kind of solution.
In your case, Martijn Pieters solution is probably the best, but I was considering what you would do if you needed to do this for any number of parameters, and needed to do some ascending and some descending.
This approach creates a function to generate a sort index on the fly. Calling getsortfunction with the list of tuples to sort and a list containing the indices and if they should be in reverse order (for example
(2,True) means second index in reverse order), returns a function which creates the sort index for an object. It is pretty ugly, but versatile.
def getsortfunction(values,indices): sorts = [sorted(list(set(x[indices[i]] for x in values)),reverse=indices[i]) for i in range(len(indices))] def sortfunction(y): return tuple(sorts[i].index(y[indices[i]]) for i in range(len(indices))) return sortfunction
a = [('Pineapple',1),('Orange',3),('Banana',1),('Apple',1),('Cherry',2)] # sort a by index 1 first (in reverse order) and then by index 0 in non-reverse order b = sorted(a,key=getsortfunction(a,[(1,True),(0,False)])) # gives desired list
With an additional criteria
c = [('Pineapple',1,'Hawaii'),('Orange',3,'Florida'),('Banana',1,'Hawaii'),('Apple',1,'Washington'),('Cherry',2,'Washington')] # sort first by number (in reverse order) then by state, and finally by fruit d = sorted(c,key=getsortfunction(c,[(1,True),(2,False),(0,False)])) # sort c first by number (in reverse order), then by fruit, ignoring state e = sorted(c,key=getsortfunction(c,[(1,True),(0,False)]))
The getsortfunction first builds a nested list of unique values in order and returns a function which maps each value to be sorted to a numeric tuple giving its indices in the sorted value list.
The biggest advantage of this is that you can determine the sort criteria at runtime (for instance from user requests).
Have your key return a tuple of the numeric value negated, and then the string. By negating, your numbers will be sorted in descending order, while the strings are sorted in ascending order:
top_n.sort(key=lambda t: (-t, t))
Yes, this is a bit of a hack, but works anywhere you need to sort by two criteria in opposite directions, and one of those criteria is numeric.
>>> top_n = [('Pineapple', 1), ('Orange', 3), ('Banana', 1), ('Apple', 1), ('Cherry', 2)] >>> sorted(top_n, key=lambda t: (-t, t)) [('Orange', 3), ('Cherry', 2), ('Apple', 1), ('Banana', 1), ('Pineapple', 1)]