pythonsortingasort

sort a python list while maintaining its element's indices


I need to sort a python list but the sort should not alter the index the elements had before the sort. There's a function called asort for this in php, but how to do it in python? Is there such a method? If not, how to do it in fewer line?

For instance, take following example:

$a = [1, 5, 2, 4];
print_r($a);
asort($a);
print_r($a);

Output will be:

Array
(
    [0] => 1
    [1] => 5
    [2] => 2
    [3] => 4
)
Array
(
    [0] => 1
    [2] => 2
    [3] => 4
    [1] => 5
)

Here 5 still has index 1 after asort but it has been sorted to the end.

I do have my own solution, which is: Create the indices as a list of integers. Then use [List Length] and [Series] to create this list. Then, sort both numeric values and indices synchronously, i.e. using the same Sort component. Although, this is not exactly what asort() does, but pretty close.

Is there a better way to do it? That too in fewer lines? Maybe a built-in method? An efficient way can be preferred over fewer lines.

Thanks


Solution

  • The primary difference between PHP and Python is that PHP arrays are both associative and ordered, while in Python you can either have an ordered list or an associative dict, but not both in one data structure.*

    * There's OrderedDict and dict is starting to become ordered itself, but let's stick with the primitives.

    So fundamentally you'll need to think of different data structures. The typical way to do this is to have a list of tuples, with one tuple value representing your former index and the other value the former value:

    [(0, 'foo'), (1, 'bar'), ...]
    

    You can get to that from a normal list using enumerate:

    l = list(enumerate(my_list))  # ['foo', 'bar'] → [(0, 'foo'), (1, 'bar')]
    

    And you can sort it in the process:

    l = sorted(enumerate(my_list), key=lambda i: i[1])  # → [(1, 'bar'), (0, 'foo')]
    

    Here lambda i: i[1] simply sorts by the second tuple value, i.e. your former value. You can replace that with itemgetter(1) from the operator module for brevity, or adjust as necessary for your sorting condition.

    If you want to turn that into an associative data structure again, use OrderedDict:

    from collections import OrderedDict
    from operator import itemgetter
    
    l = OrderedDict(sorted(enumerate(my_list), key=itemgetter(1)))