3

When we want to sort a list of tuples, where each tuple is of length 3 (all increasing), we right the following code

ts = [(7, 14, 5), (3, 12, 9), (20, 22, 8), (20, 23, 24), (3, 12, 8), (4, 5, 7), (4, 5, 6), (4, 22, 17)]
ts.sort(key=lambda t: (t[0], t[1], t[2]))

By this I mean, that initially the elements at position zero are compared, lower the zero element in a tuple, the closer to the beginning it is. If two tuples have the same value at position zero, element with lower second element would be closer to the left. If two tuples have the same second element, third elements in a tuple are considered.

So the output of the above code is

Output: [(3, 12, 8), (3, 12, 9), (4, 5, 6), (4, 5, 7), (4, 22, 17), (7, 14, 5), (20, 22, 8), (20, 23, 24)]

But what if I want to reverse the ordering in some cases (not all), for example compare first elements, and if one tuple has smaller first element it is earlier in the sorted list. But if the first elements are the same, tuple with larger first element would be earlier in the list. And if the second elements in a tuple are the same, tuple with larger first element would be earlier in the list.

This could also be described as:

  • First consider first elements in a tuple, sort them in increasing order

  • If first elements in a tuple are the same, sort them in decreasing order

  • If second elements in a tuple are the same, sort them in decreasing order

So, the input I provided should be

Output: [(3, 12, 9), (3, 12, 8), (4, 22, 17), (4, 5, 7), (4, 5, 6), (7, 14, 5), (20, 23, 24), (20, 22, 8)]

I want to know, whether this could be done using a lambda function, or there has to be a separate method for the desired sorting.

We can also generalize this problem to a tuple of length n. What if we have a list of length n like

('increasing', 'decreasing', 'decreasing', ..., 'increasing', 'decreasing')

This would mean:

  • First consider first elements in a tuple, sort them in increasing order

  • If first elements in a tuple are the same, sort them in decreasing order

  • If second elements in a tuple are the same, sort them in decreasing order

  • If elements at position n - 2 in a tuple are the same, sort them in increasing order

  • if elements at position n - 1 in a tuple are the same, sort them in decreasing order

I would be happy, to see the solution to the problem about tuples of length 3, and discussion for the generalized problem, where the length of a tuple is n.

vnikonov_63
  • 191
  • 12
  • your first example is not needed at all, tuples sort by order of elements in increasing order by default. – Patrick Artner Jun 10 '20 at 20:44
  • See [python-sort-list-of-lists-ascending-and-then-decending](https://stackoverflow.com/questions/6666748/python-sort-list-of-lists-ascending-and-then-decending) and [how-to-write-python-sort-key-functions-for-descending-values](https://stackoverflow.com/questions/11206884/how-to-write-python-sort-key-functions-for-descending-values) and plenty others – Patrick Artner Jun 10 '20 at 20:46

1 Answers1

5

You can change the signs in the tuples' values to get the expected behaviour:

ts.sort(key=lambda t: (t[0], -t[1], -t[2]))

print(ts)
# [(3, 12, 9), (3, 12, 8), (4, 22, 17), (4, 5, 7), (4, 5, 6), (7, 14, 5), 
#  (20, 23, 24), (20, 22, 8)]

For the general case, you can map the the increasing', 'decreasing'... list to sings and zip each tuple in the key with the signs as:

l = ('increasing', 'decreasing', 'decreasing')
d = {'increasing':1, 'decreasing':-1}
signs = [d[i] for i in l]
ts.sort(key = lambda x: tuple(i*sign for sign,i in zip(signs, x)))

Which would yield the same as above:

print(ts)
# [(3, 12, 9), (3, 12, 8), (4, 22, 17), (4, 5, 7), (4, 5, 6), (7, 14, 5), 
#  (20, 23, 24), (20, 22, 8)]
yatu
  • 86,083
  • 12
  • 84
  • 139