2

I have a list of tuples, where each tuple(a,b) represents the height of an object a*b:

L = [("3m", "5cm"), ("10m", "77cm"), ("1m", "82cm"), ("1m", "74cm"), ("11m", "50cm")] 

Now, I want to sort this list based on height using a lambda sort function. The output is supposed to look as follows:

L = [("1m", "74cm"), ("1m", "82cm"), ("3m", "5cm"), ("10m", "77cm"), ("11m", "50cm")]

The follwing is what I tried, but I am getting errors:

L.sort(key= lambda x: 100 * int(x[0][0:-2]) + int(x[1][0:-3])) 

How can it be fixed?

DataBach
  • 1,330
  • 2
  • 16
  • 31

4 Answers4

2

Your slice indices are off; the end index is exclusive, so you wanted to slice to index -1 and -2 respectively, making it:

L.sort(key=lambda x: 100 * int(x[0][:-1]) + int(x[1][:-2])) 

I also omitted the start index (since it defaults to zero for forward slices anyway). Another solution would be to rstrip so you're not performing explicit slicing with magic numbers:

L.sort(key=lambda x: 100 * int(x[0].rstrip('m')) + int(x[1].rstrip('cm'))) 

Note that rstrip will strip runs of any of the provided characters, so this might misbehave if you had values that didn't end with the expected m and cm, but then, the magic slicing would silently misbehave more often, so this would still be an improvement.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
1

Your error is due to an incorrect slicing of the strings. It is always useful to break down these problems into smaller parts. When slicing the first string:

print(L[0][0][0:-2])
''

This returns an empty string. Following your notation it should actually be L[0][0][0:-1], which can be simplified to L[0][0][:-1]. This is because with python slice notation a[start:stop], the last element of a slice is not included. I suggest you check this post so you have a better understanding of how slice notation works. So you need:

sorted(L, key = lambda x: int(x[0][:-1])*100 + int(x[1][:-2]))

[('1m', '74cm'),
 ('1m', '82cm'),
 ('3m', '5cm'),
 ('10m', '77cm'),
 ('11m', '50cm')]
yatu
  • 86,083
  • 12
  • 84
  • 139
1

Use replace to m and cm or any sister method like .strip("cm")

 L.sort(key= lambda x: 100 * int(x[0].replace('m','')) + int(x[1].replace('cm','')))
Charif DZ
  • 14,415
  • 3
  • 21
  • 40
  • 2
    Negative indices remove the need to know the length; it's brittle, but it would work if the inputs reliably ended in only `m` or `cm` and the end index is fixed. – ShadowRanger Sep 05 '19 at 11:03
1

You don't need to convert each tuple into a single integer; a tuple of integers will suffice (assuming, alas, that you don't have any weird heights like ("1m", "105cm")).

L.sort(key=lambda x: (int(x[0].strip("cm")), int(x[1].strip("cm"))))

Lists sort the same as tuples, so you can simplify this with a list comprehension.

L.sort(key=lambda p: [int(x.strip("cm")) for x in p])

Both tuples and lists compare lexicographically: one element at a time, with the first pair of non-equal elements determining the result.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • I would not have expected that; might be an implementation detail based on how many name lookups have to occur. – chepner Sep 05 '19 at 12:39
  • My bad, it is the other way around. Converting to integers is about 0.000023 seconds faster than using the list comprehension. I still appreciate you explanation though, how tuples and lists compare. – DataBach Sep 05 '19 at 12:49