Python сортирует строки с цифрами в конце

Какой самый простой способ отсортировать список строк с цифрами в конце, где некоторые имеют 3 цифры, а некоторые имеют 4:

>>> list = ['asdf123', 'asdf1234', 'asdf111', 'asdf124']
>>> list.sort()
>>> print list
['asdf111', 'asdf123', 'asdf1234', 'asdf124']

следует поставить 1234 один на конец. Есть простой способ сделать это?

8 ответов

Решение

Есть простой способ сделать это?

нет

Совершенно неясно, каковы настоящие правила. "У некоторых есть 3 цифры, а у некоторых 4", на самом деле не очень точная или полная спецификация. Все ваши примеры показывают 4 буквы перед цифрами. Это всегда правда?

import re
key_pat = re.compile(r"^(\D+)(\d+)$")
def key(item):
    m = key_pat.match(item)
    return m.group(1), int(m.group(2))

Тот key Функция может делать то, что вы хотите. Или это может быть слишком сложно. Или, может быть, картина действительно r"^(.*)(\d{3,4})$" или, может быть, правила еще более неясны.

>>> data= ['asdf123', 'asdf1234', 'asdf111', 'asdf124']
>>> data.sort( key=key )
>>> data
['asdf111', 'asdf123', 'asdf124', 'asdf1234']

Есть простой способ сделать это?

да

Вы можете использовать модуль natsort.

>>> from natsort import natsorted
>>> natsorted(['asdf123', 'asdf1234', 'asdf111', 'asdf124'])
['asdf111', 'asdf123', 'asdf124', 'asdf1234']

Полное раскрытие, я автор пакета.

То, что вы, вероятно, описываете, называется естественной сортировкой или человеческой сортировкой. Если вы используете Python, вы можете позаимствовать у реализации Неда.

Алгоритм естественной сортировки выглядит примерно так:

  • Разбейте каждое значение на алфавитные "куски" и числовые "куски"
  • Сортировать по первому фрагменту каждого значения
    • Если кусок в алфавитном порядке, сортируйте его как обычно
    • Если блок является числовым, сортируйте по числовому значению, представленному
  • Возьмите значения, которые имеют один и тот же первый блок, и отсортируйте их по второму фрагменту.
  • И так далее

Вам нужна ключевая функция. Вы готовы указать 3 или 4 цифры в конце, и я чувствую, что вы хотите, чтобы они сравнивались численно.

sorted(list_, key=lambda s: (s[:-4], int(s[-4:])) if s[-4] in '0123456789' else (s[:-3], int(s[-3:]))) 

Без лямбды и условного выражения это

def key(s):
    if key[-4] in '0123456789':
         return (s[:-4], int(s[-4:]))
    else:
         return (s[:-3], int(s[-3:]))

sorted(list_, key=key)

Это просто использует тот факт, что кортежи сортируются по первому элементу, а затем по второму. Так потому что key Функция вызывается для получения значения для сравнения, теперь элементы будут сравниваться как кортежи, возвращаемые функцией key. Например, 'asdfbad123' будет сравнивать с 'asd7890' как ('asdfbad', 123) сравнивается с ('asd', 7890), Если последние 3 символа строки на самом деле не являются цифрами, вы получите ValueError, который совершенно уместен, учитывая тот факт, что вы передали ему данные, которые не соответствуют спецификациям, для которых он был разработан.

l = ['asdf123', 'asdf1234', 'asdf111', 'asdf124']
l.sort(cmp=lambda x,y:cmp(int(x[4:]), int(y[4:]))

Проблема в том, что сортировка здесь алфавитная, так как они являются строками. Каждая последовательность символов сравнивается перед переходом к следующему символу.

>>> 'a1234' < 'a124'  <----- positionally '3' is less than '4' 
True
>>> 

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

>>> x = ['asdf123', 'asdf1234', 'asdf111', 'asdf124']
>>> y = [ int(t[4:]) for t in x]
>>> z = sorted(y)
>>> z
[111, 123, 124, 1234]
>>> l = ['asdf'+str(t) for t in z]
>>> l
['asdf111', 'asdf123', 'asdf124', 'asdf1234']
>>> 

Вместо того, чтобы разбивать каждую строку самостоятельно, я прошу python сделать это для меня с re.findall():

import re
import sys

def SortKey(line):
  result = []
  for part in re.findall(r'\D+|\d+', line):
    try:
      result.append(int(part, 10))
    except (TypeError, ValueError) as _:
      result.append(part)
  return result

print ''.join(sorted(sys.stdin.readlines(), key=SortKey)),
L.sort(key=lambda s:int(''.join(filter(str.isdigit,s[-4:]))))
Другие вопросы по тегам