7

I often do sorts in Python using lambda expressions, and although it works fine, I find it not very readable, and was hoping there might be a better way. Here is a typical use case for me.

I have a list of numbers, e.g., x = [12, 101, 4, 56, ...]

I have a separate list of indices: y = range(len(x))

I want to sort y based on the values in x, and I do this:

y.sort(key=lambda a: x[a])

Is there a good way to do this without using lambda?

PaulMcG
  • 62,419
  • 16
  • 94
  • 130
new name
  • 15,861
  • 19
  • 68
  • 114
  • Any particular reason why a sorted list of the original values wouldn't suffice? `x_sorted = sorted(x)` – MattH Aug 04 '10 at 16:12
  • 1
    The OP wants the indexes of the elements in sorted order, not the elements themselves. – PaulMcG Aug 04 '10 at 16:23
  • 1
    It is a shame that `lambda` is such a long and ugly keyword. This is pretty much the way to do it. – PaulMcG Aug 04 '10 at 16:25
  • Looks very much similar than the http://stackoverflow.com/questions/3382352/equivalent-of-numpy-argsort-in-basic-python where I answered to give 'rank order list' solution, unfortunately with lambda. For me I prefer it though to the itemgetter solution. – Tony Veijalainen Aug 04 '10 at 20:23

4 Answers4

12

You can use the __getitem__ method of the list x. This behaves the same as your lambda and will be much faster since it is implemented as a C function instead of a python function:

>>> x = [12, 101, 4, 56]
>>> y = range(len(x))
>>> sorted(y, key=x.__getitem__)
[2, 0, 3, 1]
Dave Kirby
  • 25,806
  • 5
  • 67
  • 84
7

Not elegantly, but:

[a for (v, a) in sorted((x[a], a) for a in y)]

BTW, you can do this without creating a separate list of indices:

[i for (v, i) in sorted((v, i) for (i, v) in enumerate(x))]
Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
5

I'm not sure if this is the kind of alternative you meant, but you could define the key function with a def:

def sort_key(value):
    return x[value]

y.sort(key = sort_key)

Personally, I think this is worse than the lambda as it moves the sort criteria away from the line of code doing the sort and it needlessly adds the sort_key function into your namespace.

David Webb
  • 190,537
  • 57
  • 313
  • 299
0

I suppose if I wanted to create another function, I could do it something like this (not tested):

def sortUsingList(indices, values):
    return indices[:].sort(key=lambda a: values[a])

Though I think I prefer to use lambda instead to avoid having to create an extra function.

new name
  • 15,861
  • 19
  • 68
  • 114