2

I want to sort a list of tuples which contain (string, integer, integer). The first value of these tuples can be any string (for example '$', or a numeric string such as '9'). The goal is to sort according to the first element of these tuples, if the comparison occurs between two identical strings I sort according to the second element. I tried the following approach but it turned out to be unsuccessful. Solutions?

array = [('$', 0, 0), ('3', 3, 3), ('7', 5, 6), ('15', 6, 9), ('5', 7, 11), ('17', 8, 13), ('18', 9, 16), ('19', 10, 18), ('16', 11, 20)]
sorted_array = sorted(array, key=lambda x:(x[0], int(x[1])))
print(sorted_array)

Output:

[('$', 0, 0), ('3', 3, 3), ('7', 5, 6), ('15', 6, 9), ('5', 7, 11), ('17', 8, 13), ('18', 9, 16), ('19', 10, 18), ('16', 11, 20)]

I want to get:

[('$', 0, 0), ('3', 3, 3), ('5', 7, 11), ('7', 5, 6), ('15', 6, 9), ('16', 11, 20), ('17', 8, 13), ('18', 9, 16), ('19', 10, 18)]

2 Answers2

5

Using natsort (natural sorting) and str.isdigit you can sort numbers naturally while keeping non numbers first. But the more general rule you might expect is unclear.

from natsort import natsorted 

array = [('$', 0, 0), ('3', 3, 3), ('7', 5, 6), ('15', 6, 9), ('5', 7, 11), ('17', 8, 13), ('18', 9, 16), ('19', 10, 18), ('16', 11, 20)]
sorted_array = natsorted(array, key=lambda x:(x[0].isdigit(), x))
print(sorted_array)

Output:

[('$', 0, 0), ('3', 3, 3), ('5', 7, 11), ('7', 5, 6), ('15', 6, 9), ('16', 11, 20), ('17', 8, 13), ('18', 9, 16), ('19', 10, 18)]
Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
mozway
  • 194,879
  • 13
  • 39
  • 75
2

You can get that result with a regular sort if you right justify the string component in the sort key.

array = [('$', 0, 0), ('3', 3, 3), ('7', 5, 6), ('15', 6, 9), 
         ('5', 7, 11), ('17', 8, 13), ('18', 9, 16), ('19', 10, 18), 
         ('16', 11, 20)]

sorted_array = sorted(array,key=lambda x:(x[0].rjust(15),x[1:]))

print(sorted_array)
[('$', 0, 0), ('3', 3, 3), ('5', 7, 11), ('7', 5, 6), ('15', 6, 9), 
 ('16', 11, 20), ('17', 8, 13), ('18', 9, 16), ('19', 10, 18)]
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • 1
    This is a nice hack (+1), but you need to estimate the upper bound of the numbers you'll evaluate. Also generating the strings adds a extra cost. ;) – mozway Dec 05 '21 at 20:38