Сортировать список питонов, сохраняя индексы его элементов

Мне нужно отсортировать список Python, но сортировка не должна изменять индекс, который элементы имели до сортировки. Для этого в php есть функция asort, но как это сделать в python? Есть ли такой метод? Если нет, то как сделать это в меньшем количестве строк?

Например, возьмем следующий пример:

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

Выход будет:

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

Здесь 5 все еще имеет индекс 1 после asort но это было отсортировано до конца.

У меня есть собственное решение: создать индексы в виде списка целых чисел. Затем используйте [List Length] и [Series], чтобы создать этот список. Затем выполните сортировку как числовых значений, так и индексов синхронно, т. Е. Используя один и тот же компонент сортировки. Хотя, это не совсем то, что asort() делает, но довольно близко.

Есть ли лучший способ сделать это? Это тоже в меньшем количестве строк? Может быть встроенный метод? Эффективный способ может быть предпочтительным по сравнению с меньшим количеством линий.

Спасибо

3 ответа

Решение

Основное различие между PHP и Python состоит в том, что массивы PHP являются ассоциативными и упорядоченными, в то время как в Python вы можете иметь упорядоченный list или ассоциативный dict, но не оба в одной структуре данных. *

* Есть OrderedDict а также dict начинает упорядочивать себя, но давайте придерживаться примитивов.

Так что в основном вам нужно думать о различных структурах данных. Типичный способ сделать это - создать список кортежей, в котором одно значение кортежа представляет ваш предыдущий индекс, а другое - прежнее значение:

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

Вы можете добраться до этого из обычного списка, используя enumerate:

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

И вы можете отсортировать его в процессе:

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

Вот lambda i: i[1] просто сортирует по второму значению кортежа, т.е. вашему прежнему значению. Вы можете заменить это на itemgetter(1) от operator модуль для краткости, или отрегулируйте по мере необходимости для вашего условия сортировки.

Если вы хотите снова превратить это в ассоциативную структуру данных, используйте OrderedDict:

from collections import OrderedDict
from operator import itemgetter

l = OrderedDict(sorted(enumerate(my_list), key=itemgetter(1)))

Сначала вы подключаете исходные значения к его списку индексов. Затем вы сортируете его, и исходные индексы привязываются к новым отсортированным значениям. Наконец распакуйте их, чтобы получить оба списка.

i_xs                 = [(x, i) for (i, x) in enumerate(xs)]
s                    = sorted(i_xs)
sorted_xs, index_lst = unzip(s)

Используя это

def unzip(ls):
    if isinstance(ls, list):
        if not ls:
            return [], []
        else:
            xs, ys = zip(*ls)

        return list(xs), list(ys)
    else:
        raise TypeError

Пример. В:

[34, 23424, 1212, -2324, 34353]

Из:

[-2324, 34, 1212, 23424, 34353]

[3, 0, 2, 1, 4]

Давайте сделаем это с этим примером списка:

l = [2, 1, 4, 3]

Вы можете легко отсортировать его, сохранив индексы и значения в кортеже:

l2 = [(i, l.index(i)) for i in l]
l2 = sorted(l2, key=lambda x: x[0])

Это отсортирует новый список в порядке возрастания, сохраняя значение в качестве первого элемента кортежа и старый индекс в качестве второго элемента. Обратите внимание, что это не будет работать, если у вас есть идентичные значения в вашем первоначальном списке, как index() вернет первый случай!

Вот так:

>>> l2
[(1, 1), (2, 0), (3, 3), (4, 2)]
Другие вопросы по тегам