Сначала сортируйте список с самыми длинными пунктами

Я использую лямбду, чтобы изменить поведение сортировки.

sorted(list, key=lambda item:(item.lower(),len(item))) 

Сортируя список, содержащий элементы A1,A2,A3,A,B1,B2,B3,B , результат A,A1,A2,A3,B,B1,B2,B3 .

Мой ожидаемый отсортированный список будет A1,A2,A3,A,B1,B2,B3,B

Я уже пытался включить len(item) для сортировки, который не работал. Как изменить лямбда, чтобы вместо этого был результат сортировки?

Вот один из способов сделать это:

 >>> import functools >>> def cmp(s, t): 'Alter lexicographic sort order to make longer keys go *before* any of their prefixes' ls, lt = len(s), len(t) if ls < lt: s += t[ls:] + 'x' elif lt < ls: t += s[lt:] + 'x' if s < t: return -1 if s > t: return 1 return 0 >>> sorted(l, key=functools.cmp_to_key(cmp)) ['A1', 'A2', 'A3', 'A', 'B1', 'B2', 'B3', 'B'] 

Традиционно лексикографический порядок сортировки длиннее строк после идентичных им префиксов (т.е. «abc» идет до «abcd»).

Чтобы удовлетворить ваши ожидания сортировки, мы сначала «фиксируем» более короткую строку, добавляя оставшуюся часть более длинной строки плюс другой символ, чтобы сделать ее более длинной:

 compare abc to defg --> compare abcgx to defg compare a to a2 --> compare a2x to a2 

Инструмент functools.cmp_to_key () затем преобразует функцию сравнения в ключевую функцию.

Это может показаться большой работой, но ожидания рода очень противоречат встроенным правилам лексикографической сортировки.

FWIW, вот еще один способ его написания, который может или не может считаться более ясным:

 def cmp(s, t): 'Alter lexicographic sort order to make longer keys go *before* any of their prefixes' for p, q in zip(s, t): if p < q: return -1 if q < p: return 1 if len(s) > len(t): return -1 elif len(t) > len(s): return 1 return 0 

Логика такова:

  • Сравните символ по символу, пока не будет найдена другая пара
  • Эта различная пара определяет порядок сортировки традиционным способом
  • Если нет другой пары, то самый длинный вход идет первым.
  • Если нет другой пары, а длины равны, то строки равны.

Мой первый ответ: просто len критерий len чтобы отменить только этот критерий.

 sorted(list, key=lambda item:(item.lower(),-len(item))) # doesn't work! 

Но это не работает, потому что есть конфликт между альфа-сортировкой и длиной. Сорт Alpha сначала помещает маленькие строки. Таким образом, критерий длины не работает.

Вам необходимо объединить оба критерия. Между ними нет четкого приоритета.

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

 l = ["A","B","A1","A2","A3","B1","B2","B3"] maxlen = max(len(x) for x in l) print(sorted(l, key=lambda item:item.lower()+chr(127)*(maxlen-len(item)))) 

результат:

 ['A1', 'A2', 'A3', 'A', 'B1', 'B2', 'B3', 'B'] 

BTW не называют список list по очевидным причинам.

Можно было бы построить ключ, взяв:

  1. первая буква каждого пункта
  2. длина
  3. сам предмет

Например:

 >>> L = ['A1', 'B2', 'A', 'A2', 'B1', 'A3', 'B3', 'B'] >>> print(sorted(L, key = lambda item: (item[0], -len(item), item))) ['A1', 'A2', 'A3', 'A', 'B1', 'B2', 'B3', 'B'] 

Мне нравится Tries, поэтому просто для удовольствия я написал решение на основе Trie:

 class Trie(): def __init__(self): self.data = {} def add(self, word): ref = self.data for char in word: ref[char] = char in ref and ref[char] or {} ref = ref[char] ref[''] = 1 def sorted_print(dct, prefix=''): sorted_keys = sorted(filter(bool, dct.keys()), key=str.lower) for key in sorted_keys: v = dct[key] if isinstance(v, dict): sorted_print(v, prefix + key) if '' in dct: print(prefix) my_list = ["B1", "B3", "B2", "A1", "A2", "A3", "A", "B"] t = Trie() for w in my_list: t.add(w) sorted_print(t.data) # A1 # A2 # A3 # A # B1 # B2 # B3 # B 

Это должно работать для любой строки любой длины.

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