问题描述:

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[1] in descending order, then if the tuple values (tuple[1]) match like forApple, Banana & Pineapple - list should be further sorted based on tuple[0] 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)]

as "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][0]] for x in values)),reverse=indices[i][1]) for i in range(len(indices))]
    def sortfunction(y):
        return tuple(sorts[i].index(y[indices[i][0]]) for i in range(len(indices)))
    return sortfunction

Examples

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[1], t[0]))

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.

Demo:

>>> top_n = [('Pineapple', 1), ('Orange', 3), ('Banana', 1), ('Apple', 1), ('Cherry', 2)]
>>> sorted(top_n, key=lambda t: (-t[1], t[0]))
[('Orange', 3), ('Cherry', 2), ('Apple', 1), ('Banana', 1), ('Pineapple', 1)]
相关阅读:
Top