Я пытаюсь отсортировать список, который содержит цифры и буквы:

names = ["5aG", "6bG", "10cG", "J1", ...]

Вывод должен выглядеть так:

['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR','10aG', '10bG', '10cG', '10aR', 'J1', 'J2']

Первым элементом строки всегда является число от 5 до 10, затем идет буква от a - c, а в конце - еще одна буква («G» или «R»).

Кроме того, есть строки "J1" и "J2". Они должны быть всегда последними («J1» перед «J2»).

Как я могу добиться чего-то подобного? Я думал об использовании лямбда-функции.

До сих пор я жестко закодировал это, но я думаю, что должно быть лучшее решение.
Это моя жестко закодированная версия:

classes = ['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR','10aG', '10bG', '10cG', '10aR', 'J1', 'J2']

def s(v):
  """Get index of element in list"""
  try:
    return classes.index(v)
  except ValueError:
return 500

l = ['5bG', '6aG', '6bG', '8aR', '9aG', '9bG', '9aR', '10cG', '10aR', 'J1', 'J2', '5aG', '']
w = sorted( l, key=s)
print(w)
3
Developer 1 Мар 2018 в 00:20

5 ответов

Лучший ответ

Вы можете использовать re для извлечения переднего целого числа, а затем полагаться на tuple сравнение.

import re

def key(s):
    num, letters = re.match(r'(\d*)(.*)', s).groups()
    return float(num or 'inf'), letters

sorted_names = sorted(names, key=key)

Обратите внимание, как вы можете положиться на float('inf'), чтобы получить ваши токены без префиксных цифр до конца.

1
Olivier Melançon 28 Фев 2018 в 22:26

Вы можете попробовать это:

После скремблирования желаемого результата:

import re
s = ['5aR', '7aR', '10aR', '10cG', '9bG', '8aR', '8bG', '6bR', '5aG', '9aG', 'J1', '6aR', '6aG', '5bR', '7aG', '7bG', '9aR', '5bG', 'J2', '6bG', '10bG', '8aG', '10aG', '6cG']
c, d, *h = sorted(s, key=lambda x:[False if not x[0].isdigit() else int(re.findall('^\d+', x)[0]), x[-1], x[-2]])
sorted_result = [*h, c, d]

Выход:

['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR', '10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
1
Ajax1234 28 Фев 2018 в 21:26

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

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

Большинство ваших строк представляют собой целое число, за которым следуют две буквы. Остальные, которые вы хотите видеть последними, это либо "J1", либо "J2". Следующее должно быть подходящей ключевой функцией. Я принимаю меры предосторожности при применении функции int к числам, чтобы гарантировать, что они сортируются по числовой, а не по лексикографической (потому что '2' > '10').

def key_func(s):
    # Ensure J-strings are at the end
    if s.startswith('J'):
        return (1000000, 'J', int(s[1:]))
    else:
        # The rest, split into digits and two characters
        return (int(s[:-2]), s[-2], s[-1])

При тестировании с рандомизированной копией ваших данных результат

data = ['8aG', '5aR', '6aG', '10aG', '6cG', '8bG', '9aG',
        '5aG', '6bG', '7aR', 'J1', '10cG', '10bG', '10aR',
        '6bR', 'J2', '6aR', '8aR', '7aG', '9aR', '5bR',
        '9bG', '7bG', '5bG']
print(sorted(data, key=key_func))

Кажется правильным (разрывы строк вставлены для удобства чтения):

['5aG', '5aR', '5bG', '5bR', '6aG', '6aR', '6bG', '6bR',
 '6cG', '7aG', '7aR', '7bG', '8aG', '8aR', '8bG', '9aG',
 '9aR', '9bG', '10aG', '10aR', '10bG', '10cG', 'J1', 'J2']
0
holdenweb 28 Фев 2018 в 21:58

С пользовательской функцией compound_sort():

import re

lst = ['9bG', '9aR', 'J2', '7bG', '7aG', '6bR', 'J1', '6cG', '6aG', '6bG', '5bG', '5aG', '8bG', '5bR', '8aR', '5aR', '10aR', '6aR', '10bG', '10aG', '9aG', '10cG', '7aR', '8aG']
pat = re.compile(r'(\d+)(.*)|(J)(\d+)')

def compound_sort(t):
  t = tuple(filter(None, t))    # filter empty(None) matches
  return (int(t[0]),) + t[1:] if t[0] != 'J' else (float('inf'), t[1])

result = sorted(lst, key=lambda x: compound_sort(pat.search(x).groups()))
print(result)

Выход:

['5aG', '5aR', '5bG', '5bR', '6aG', '6aR', '6bG', '6bR', '6cG', '7aG', '7aR', '7bG', '8aG', '8aR', '8bG', '9aG', '9aR', '9bG', '10aG', '10aR', '10bG', '10cG', 'J1', 'J2']
0
RomanPerekhrest 28 Фев 2018 в 22:04

Вот один из способов.

lst = ['7aR', '9aG', '7bG', '10cG', '5bG', '6aG', '6bG', '10bG', 'J2', '5aR', '10aG', '9bG', '6aR', '7aG', '10aR', '9aR', '8aR', 'J1', '5bR', '6bR', '5aG', '8bG', '6cG', '8aG']

sorted([i for i in lst if i[0]!='J'], key=lambda x: [int(x[:-2]), x[-1], x[-2]]) + \
sorted(i for i in lst if i[0]=='J')

# ['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR', '10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
0
jpp 28 Фев 2018 в 21:37