Home > Mobile >  Sorting an array of tuples in Python using multiple criteria both descending and ascending in value
Sorting an array of tuples in Python using multiple criteria both descending and ascending in value

Time:09-30

Assuming you have a list of tuples and the need to sort them based on 2 values of the tuple where one should be descending in value and the other ascending in value as second priority. For example, the following code will sort based on the priority of the first value first, then afterwards based on the second value but both being descending in value due to the reversed=True. The priority needs to be remained, but the second value should be ascending.

my_list = [(1, 2, 3), (1, 3, 2), (2, 1, 3)]
my_list.sort(key=lambda tup: (tup[0], tup[1]), reverse=True)

>>> [(2, 1, 3), (1, 3, 2), (1, 2, 3)]

The first element of the tuple is being prioritised and sorted, then afterwards the second element is used to sort around. But both are in descending value. How can the list be sorted so that the tuple is first sorted based on the first value descending and then the second value ascending?

The desired output in this case would be >>> [(2, 1, 3), (1, 2, 3), (1, 3, 2)]

CodePudding user response:

Negate the field that should be descending:

my_list.sort(key = lambda tup: (-tup[0], tup[1]))

CodePudding user response:

You could multiply by -1 the second field:

my_list = [(1, 2, 3), (1, 3, 2), (2, 1, 3)]
my_list.sort(key=lambda tup: (tup[0], -1 * tup[1]), reverse=True)
print(my_list)

Output

[(2, 1, 3), (1, 2, 3), (1, 3, 2)]

General Solution

Note that multiplying by -1 is only going to work for numbers, for any other type of comparable objects (strings, tuples, etc), you could use a wrapper class like below:

@total_ordering
class neg:

    def __init__(self, val):
        self.val = val

    def __eq__(self, other):
        return self.val == other.val

    def __lt__(self, other):
        """Should return the inverse value of self.val.__lt__(other)"""
        return self.val >= other.val

and then use it like this:

my_list.sort(key=lambda tup: (tup[0], neg(tup[1])), reverse=True)
print(my_list)

Output (using wrapper class)

[(2, 1, 3), (1, 2, 3), (1, 3, 2)]

For more on the total_ordering class decorator, see the documentation and for the __lt__ method read this.

  • Related