Сортировать список строк по особому ключу - PullRequest
0 голосов
/ 24 мая 2018

Я должен реализовать сортировку списка строк способом, очень похожим на функцию sorted, но с одним важным отличием.Как вы знаете, функция sorted учитывает символы пробела, предшествующие цифрам, поэтому sorted(['1 ', ' 9']) даст нам [' 9', '1 '].Мне нужно sorted, который учитывает цифры перед предыдущими символами, поэтому в нашем примере результат будет ['1 ', ' 9'].

Обновление

Как я понимаю, по умолчанию поведение sorted полагаетсяпорядка символов в алфавите ascii (то есть ''.join([chr(i) for i in range(59, 127)])), поэтому я решил реализовать свой собственный алфавит ascii в функции my_ord.

Я планировал использовать эту функцию в сочетании с простой функцией my_sort в качестве клавиши для sorted,

def my_ord(c):
    punctuation1 = ''.join([chr(i) for i in range(32, 48)])
    other_stuff = ''.join([chr(i) for i in range(59, 127)])
    my_alphabet = string.digits + punctuation1 + other_stuff
    return my_alphabet.find(c)

def my_sort(w):
    return sorted(w, key=my_ord)

, например: sorted([' 1 ', 'abc', ' zz zz', '9 '], key=my_sort).

Что я ожидаю в этом случае, это ['9 ', ' 1 ', ' zz zz', 'abc'].К сожалению, результат не только не соответствует ожидаемому, более того, он время от времени отличается.

Ответы [ 4 ]

0 голосов
/ 25 мая 2018

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

import string
MY_ALPHABET = (
        string.digits
        + ''.join([chr(i) for i in range(32, 127) if chr(i) not in string.digits])
)
inp = [' 1 ', 'abc', ' zz zz', '9 ', 'a 1', 'a ']
print(inp, '-->', sorted(inp, key=lambda w: [MY_ALPHABET.index(c) for c in w]))
0 голосов
/ 24 мая 2018

Вы хотите сочетание лексической и числовой сортировки.Вы можете сделать это, разделив строку на кортеж и преобразовав цифры в int.Теперь сравнение кортежей будет рассматривать каждый элемент по своим собственным правилам сравнения.

Я использовал регулярное выражение для разбиения строки на (начальный текст, пробел, цифры, все остальное), создал int и использовалчто за ключ.если строка не соответствует шаблону, она просто возвращает исходную строку в кортеже, чтобы ее также можно было использовать для сравнения.

Я переместил пробел перед цифрой (группа (2)) послецифра, но может быть более целесообразно полностью исключить ее из сравнения.

import re

test = ['1  ', ' 9']
wanted = ['1  ', ' 9']

def sort_key(val):
    """Return tuple of (text, int, spaces, remainder) or just
    (text) suitable for sorting text lexagraphically but embedded
    number numerically"""
    m = re.match(r"(.*?)(\s*)(\d+)(.*)", val)
    if m:
        return (m.group(1), int(m.group(3)), m.group(2), m.group(4))
    else:
        return (val,)

result = sorted(test, key=sort_key)
print(test, '-->', result)
assert result == wanted, "results compare"
0 голосов
/ 25 мая 2018

Для полноты и, возможно, эффективности в крайних случаях, вот решение, использующее numpy argsort:

import numpy as np
lst = ['1 ', ' 9' , ' 4', '2 ']
order = np.argsort(np.array([s.lstrip() for s in lst]))
result = list(np.array(lst)[order])

В целом, я думаю, что использование sorted (..., key = ...) обычно лучшеи это решение имеет больше смысла, если входные данные уже являются пустым массивом.С другой стороны, он использует strip () только один раз для каждого элемента и использует numpy, поэтому вполне возможно, что для достаточно больших списков это может быть быстрее.Кроме того, он формирует порядок, в котором указывается, где каждый отсортированный элемент находился в исходном списке.

В качестве последнего комментария из предоставленного вами кода, но не из приведенного вами примера, я не уверен, что вы просто хотитеубрать начальные пробелы, или сделать больше, например, best-way-to-strip-punctuation-from-a-string-in-python или первый порядок в строке без пунктуации, а затем, если ониравны, порядок в остальном (решение tdelaney) В любом случае может быть неплохо скомпилировать шаблон, например,

import numpy as np
import re
pattern = re.compile(r'[^\w]')
lst = ['1 ', ' 9' , ' 4', '2 ']
order = np.argsort(np.array([pattern.sub('',s) for s in lst]))
result = list(np.array(lst)[order])

или:

import re
pattern = re.compile(r'[^\w]')
r = sorted(['1 ', ' 9' , ' 4', '2 '], key= lambda s: pattern.sub('',s))
0 голосов
/ 24 мая 2018

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

r = sorted(['1 ', ' 9' , ' 4', '2 '], key=str.lstrip)
# r == ['1 ', '2 ', ' 4', ' 9']

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...