Вопрос-Ответ

Is there a built in function for string natural sort?

Есть ли встроенная функция для естественной сортировки строк?

У меня есть список строк, для которых я хотел бы выполнить естественную сортировку по алфавиту.

Например, следующий список отсортирован естественным образом (то, что я хочу):

['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

И вот "отсортированная" версия приведенного выше списка (то, что я получаю, используя sorted()):

['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']

Я ищу функцию сортировки, которая ведет себя как первая.

Переведено автоматически
Ответ 1

Для этого в PyPI есть сторонняя библиотека под названием natsort (полное раскрытие, я автор пакета). В вашем случае вы можете выполнить любое из следующих действий:

>>> from natsort import natsorted, ns
>>> x = ['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']
>>> natsorted(x, key=lambda y: y.lower())
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsorted(x, alg=ns.IGNORECASE) # or alg=ns.IC
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Вы должны отметить, что natsort использует общий алгоритм, поэтому он должен работать практически для любого ввода, который вы ему вводите. Если вы хотите получить более подробную информацию о том, почему вы могли бы выбрать библиотеку для этого, а не запускать свою собственную функцию, ознакомьтесь со страницей natsortдокументации"Как это работает", в частности со страницей "Особые случаи везде!" Раздел.


Если вам нужен ключ сортировки вместо функции сортировки, используйте любую из приведенных ниже формул.

>>> from natsort import natsort_keygen, ns
>>> l1 = ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> l2 = l1[:]
>>> natsort_key1 = natsort_keygen(key=lambda y: y.lower())
>>> l1.sort(key=natsort_key1)
>>> l1
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsort_key2 = natsort_keygen(alg=ns.IGNORECASE)
>>> l2.sort(key=natsort_key2)
>>> l2
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Обновление за ноябрь 2020 г.

Учитывая, что популярный запрос / вопрос "как сортировать, как в проводнике Windows?" (или что там еще есть в браузере файловой системы вашей операционной системы), начиная с natsort версии 7.1.0, есть функция, вызываемая os_sorted именно для этого. В Windows он будет сортировать в том же порядке, что и проводник Windows, а в других операционных системах он должен сортировать как любой браузер локальной файловой системы.

>>> from natsort import os_sorted
>>> os_sorted(list_of_paths)
# your paths sorted like your file system browser

Для тех, кому нужен ключ сортировки, вы можете использовать os_sort_keygen (или os_sort_key если вам просто нужны значения по умолчанию).

Предостережение - Пожалуйста, прочтите документацию API для этой функции перед использованием, чтобы понять ограничения и как добиться наилучших результатов.

Ответ 2

Попробуйте это:

import re

def natural_sort(l):
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
return sorted(l, key=alphanum_key)

Вывод:

['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Код адаптирован отсюда: Сортировка для людей: естественный порядок сортировки.

Ответ 3

Вот гораздо более питоническая версия ответа Марка Байера:

import re

def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
return [int(text) if text.isdigit() else text.lower()
for text in _nsre.split(s)]

Теперь эту функцию можно использовать как ключ в любой функции, которая ее использует, например, list.sort, sorted, max и т.д.

В виде лямбды:

lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', s)]

Полностью воспроизводимый демонстрационный код:

import re
natsort = lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', s)]
L = ["a1", "a10", "a11", "a2", "a22", "a3"]
print(sorted(L, key=natsort))
# ['a1', 'a2', 'a3', 'a10', 'a11', 'a22']
Ответ 4
data = ['elm13', 'elm9', 'elm0', 'elm1', 'Elm11', 'Elm2', 'elm10']

Давайте проанализируем данные. Разрядность всех элементов равна 2. И в общей буквенной части есть 3 буквы 'elm'.

Итак, максимальная длина элемента равна 5. Мы можем увеличить это значение, чтобы убедиться (например, до 8).

Имея это в виду, у нас есть однострочное решение:

data.sort(key=lambda x: '{0:0>8}'.format(x).lower())

без регулярных выражений и внешних библиотек!

print(data)

>>> ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'elm13']

Объяснение:

for elm in data:
print('{0:0>8}'.format(elm).lower())

>>>
0000elm0
0000elm1
0000elm2
0000elm9
000elm10
000elm11
000elm13
python sorting