Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort at various levels in Python

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.

like image 737
Pruthvidhar Rao Nadunooru Avatar asked Jan 14 '16 07:01

Pruthvidhar Rao Nadunooru


People also ask

How do you sort a range in Python?

When it is required to sort the list based on range, the 'abs' method, the 'sum' method and the list comprehension are used using a function.

How do you sort from lowest to highest in Python?

Summary. Use the Python List sort() method to sort a list in place. The sort() method sorts the string elements in alphabetical order and sorts the numeric elements from smallest to largest. Use the sort(reverse=True) to reverse the default sort order.

How do I sort multiple values in pandas?

You can sort pandas DataFrame by one or multiple (one or more) columns using sort_values() method and by ascending or descending order. To specify the order, you have to use ascending boolean property; False for descending and True for ascending.

How do you sort multiple items in Python?

Use sorted() and a lambda expression to sort a list by two fields. Call sorted(a_list, key = k) with k as lambda x: x[i], x[j] to sort list by the i -th element and then by the j -th element.


2 Answers

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)]
like image 77
Martijn Pieters Avatar answered Sep 25 '22 21:09

Martijn Pieters


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).

like image 26
Matthew Avatar answered Sep 22 '22 21:09

Matthew