Сортировка подсписок элементов в списке, оставляющий остальное на месте

Скажем, у меня есть отсортированный список строк, как в:

['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] 

Теперь я хочу сортировать на основе конечного числового значения для B s, поэтому у меня есть:

 ['A', 'B' , 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] 

Один из возможных алгоритмов состоит в том, чтобы хешировать регулярное выражение, например regex = re.compile(ur'(B)(\d*)) , найти индексы первого и последнего B , нарезать список, отсортировать фрагмент с помощью второго регулярного выражения затем вставьте отсортированный фрагмент. Однако это кажется слишком большим количеством хлопот. Есть ли способ написать ключевую функцию, которая «оставляет элемент на месте», если он не соответствует регулярному выражению и сортирует только те элементы (подписи), которые соответствуют?

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

15 Solutions collect form web for “Сортировка подсписок элементов в списке, оставляющий остальное на месте”

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

 'AB123' -> ['AB', 123] 'CD' -> ['CD'] '456' -> ['', 456] 

Примечание. В последнем случае пустая строка '' не является строго необходимой в CPython 2.x, поскольку целые классы сортируются перед строками, но это детализация реализации, а не гарантия языка, а в Python 3.x необходимо , потому что строки и целые числа не могут сравниваться вообще.

Вы можете создать такую ​​ключевую функцию, используя понимание списка и re.split() :

 import re def trailing_digits(x): return [ int(g) if g.isdigit() else g for g in re.split(r'(\d+)$', x) ] 

Здесь он находится в действии:

 >>> s1 = ['11', '2', 'A', 'B', 'B1', 'B11', 'B2', 'B21', 'C', 'C11', 'C2'] 

 >>> sorted(s1, key=trailing_digits) ['2', '11', 'A', 'B', 'B1', 'B2', 'B11', 'B21', 'C', 'C2', 'C11'] 

Как только вы добавите ограничение, в котором только строки с определенным префиксом или префиксами имеют отсортированные цифры, число вещей становится немного сложнее.

Следующая функция строит и возвращает ключевую функцию, которая выполняет требование:

 def prefixed_digits(*prefixes): disjunction = '|'.join('^' + re.escape(p) for p in prefixes) pattern = re.compile(r'(?<=%s)(\d+)$' % disjunction) def key(x): return [ int(g) if g.isdigit() else g for g in re.split(pattern, x) ] return key 

Основное отличие здесь состоит в том, что создается предварительно скомпилированное регулярное выражение (содержащее lookbehind, построенное из прилагаемого префикса или префиксов), и возвращается ключевая функция с использованием этого регулярного выражения.

Вот несколько примеров использования:

 >>> s2 = ['A', 'B', 'B11', 'B2', 'B21', 'C', 'C11', 'C2', 'D12', 'D2'] 

 >>> sorted(s2, key=prefixed_digits('B')) ['A', 'B', 'B2', 'B11', 'B21', 'C', 'C11', 'C2', 'D12', 'D2'] 

 >>> sorted(s2, key=prefixed_digits('B', 'C')) ['A', 'B', 'B2', 'B11', 'B21', 'C', 'C2', 'C11', 'D12', 'D2'] 

 >>> sorted(s2, key=prefixed_digits('B', 'D')) ['A', 'B', 'B2', 'B11', 'B21', 'C', 'C11', 'C2', 'D2', 'D12'] 

Если вызывается без аргументов, prefixed_digits() возвращает ключевую функцию, которая ведет себя одинаково с trailing_digits :

 >>> sorted(s1, key=prefixed_digits()) ['2', '11', 'A', 'B', 'B1', 'B2', 'B11', 'B21', 'C', 'C2', 'C11'] 

Предостережения:

  1. Из-за ограничения в модуле Python относительно синтаксиса lookbhehind несколько префиксов должны иметь одинаковую длину.

  2. В Python 2.x строки, которые являются чисто числовыми, будут отсортированы численно независимо от того, какие префиксы поставляются в prefixed_digits() . В Python 3 они вызывают исключение (кроме случаев, когда вызывается без аргументов или в специальном случае key=prefixed_digits('') – который будет сортировать чисто числовые строки в численном выражении и префиксные строки в алфавитном порядке). Фиксация, которая может быть возможна со значительно более сложным регулярным выражением, но я отказался от попытки через двадцать минут.

Если я правильно понимаю, ваша конечная цель – сортировать подпоследовательности, оставляя в покое элементы, которые не являются частью подпоследовательностей.

В вашем примере подпоследовательность определяется как элементы, начинающиеся с «B» . В вашем списке примеров содержатся элементы в лексикографическом порядке, что слишком удобно и может отвлекать от поиска обобщенного решения. Давайте немного перепутаем ситуацию, используя другой список примеров. Как насчет:

 ['X', 'B2', 'B11', 'B22', 'B', 'B1', 'B21', 'C', 'Q1', 'C11', 'C2'] 

Здесь предметы больше не заказываются (по крайней мере, я пытался их организовать, чтобы они не были), ни те, которые начинаются с «B», ни другие. Однако элементы, начинающиеся с «B», по-прежнему образуют единую непрерывную подпоследовательность, занимая единый диапазон 1-6, а не разделенные диапазоны, например, как 0-3 и 6-7. Это снова может отвлекать, я рассмотрю этот аспект дальше.

Если я правильно понимаю вашу конечную цель, вы бы хотели, чтобы этот список отсортировался следующим образом:

 ['X', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'Q1', 'C11', 'C2'] 

Чтобы сделать эту работу, нам нужна ключевая функция, которая вернет кортеж, так что:

  • Первое значение:
    • Если элемент не начинается с «B», тогда индекс в исходном списке (или значение в том же порядке)
    • Если элемент начинается с «B», то индекс последнего элемента, который не начинался с «B»,
  • Второе значение:
    • Если элемент не начинается с «B», то опустите это
    • Если элемент начинается с «B», то числовое значение

Это можно реализовать так и с некоторыми doctests :

 def order_sublist(items): """ >>> order_sublist(['A', 'B2', 'B11', 'B22', 'B', 'B1', 'B21', 'C', 'C1', 'C11', 'C2']) ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] >>> order_sublist(['X', 'B2', 'B11', 'B22', 'B', 'B1', 'B21', 'C', 'Q1', 'C11', 'C2']) ['X', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'Q1', 'C11', 'C2'] """ def key(): ord1 = [0] def inner(item): if not item.startswith('B'): ord1[0] += 1 return ord1[0], return ord1[0], int(item[1:] or 0) return inner return sorted(items, key=key()) 

В этой реализации элементы сортируются по этим ключам:

 [(1,), (1, 2), (1, 11), (1, 22), (1, 0), (1, 1), (1, 21), (2,), (3,), (4,), (5,)] 

Элементы, не начинающиеся с буквы «B», сохраняют свой порядок, благодаря первому значению в ключевом корте, а элементы, начинающиеся с «B», сортируются благодаря второму значению ключевого кортежа.

Эта реализация содержит несколько трюков, которые стоит объяснить:

  • key функция возвращает кортеж из 1 или 2 элементов, как объяснялось ранее: элементы, отличные от B, имеют одно значение, элементы B – два.

  • Первое значение кортежа – это не совсем оригинальный индекс, но он достаточно хорош. Значение перед первым элементом B равно 1, все элементы B используют одно и то же значение, а значения после B каждый раз получают увеличенное значение. Так как (1,) < (1, x) < (2,) где x может быть любым, эти ключи будут отсортированы по мере их появления.

А теперь к «настоящим» трюкам 🙂

  • Что происходит с ord1 = [0] и ord1[0] += 1 ? Это метод изменения нелокального значения в функции. Если бы я использовал просто ord1 = 0 а ord1 += 1 не работал бы, потому что ord1 – это примитивное значение, определенное вне функции. Без ключевого слова global это не видимо и не переназначается. ord1 значение ord1 внутри inner функции будет ord1 внешнее примитивное значение. Но ord1 является списком, он виден внутри внутри, и его содержимое может быть изменено. Обратите внимание, что нельзя переназначить. Если вы заменили ord1[0] += 1 как ord1 = [ord1[0] + 1] что приведет к тому же значению, оно не сработает, так как в этом случае ord1 с левой стороны является локальной переменной, теневое ord1 во внешнем объеме и не изменять его значение.

  • Что происходит с key и inner функциями? Я думал, что это будет аккуратно, если ключевая функция, которую мы перейдем к sorted будет повторно использована. Эта более простая версия также работает:

     def order_sublist(items): ord1 = [0] def inner(item): if not item.startswith('B'): ord1[0] += 1 return ord1[0], return ord1[0], int(item[1:] or 0) return sorted(items, key=inner) 

    Важное различие заключается в том, что если вы хотите использовать inner дважды, оба использования будут иметь один и тот же список ord1 . Это может быть приемлемым, так как ord1[0] целочисленного значения ord1[0] не переполняется во время использования. В этом случае вы не будете использовать эту функцию дважды, и даже если бы вы, вероятно, не столкнулись с риском переполнения целочисленного числа, но в принципе, полезно сделать эту функцию чистой и многоразовой, обернув ее как я в моем первоначальном предложении. То, что делает key функция, это просто инициализировать ord1 = [0] в своей области, определить inner функцию и вернуть inner функцию. Таким образом, ord1 фактически ord1 , благодаря закрытию. Каждый раз, когда вы вызываете key() , он возвращает функцию, которая имеет свое личное, новое значение ord1 .

  • И последнее, но не менее doctests : обратите внимание на doctests : комментарий """ ... """ – это больше, чем просто документация, это исполняемые тесты. Линии >>> – это код для выполнения в оболочке Python, а следующие строки – ожидаемый вывод. Если у вас есть эта программа в файле script.py , вы можете запустить тесты с помощью python -m doctest script.py . Когда все тесты проходят, вы не получаете выход. Когда тест выходит из строя, вы получаете хороший отчет. Это отличный способ проверить, работает ли ваша программа с помощью демонстрационных примеров. Вы можете иметь несколько тестовых примеров, разделенных пустыми линиями, чтобы охватить интересные угловые случаи. В этом примере есть два тестовых примера с исходным сортированным входом и измененным несортированным входом.

Однако, как @ zero-piraeus сделал интересное замечание:

Я вижу, что ваше решение зависит от sorted() сканирования списка слева направо (что разумно – я не могу представить, что TimSort будет заменен или радикально изменен в ближайшее время – но не гарантируется Python AFAIK и есть алгоритмы сортировки, которые не работают так).

Я старался быть самокритичным и сомневался, что сканирование слева направо разумно. Но я думаю, что так оно и есть. В конце концов, сортировка действительно происходит на основе ключей, а не фактических значений. Я думаю, что, скорее всего, Python делает что-то вроде этого:

  1. Возьмите список значений ключа с помощью [key(value) for value in input] , посетив значения слева направо.
  2. zip список ключей с оригинальными элементами
  3. Примените любой алгоритм сортировки по zipped-списку, сравнив элементы по первому значению zip и swapping items
  4. В конце верните отсортированные элементы с return [t[1] for t in zipped]

При построении списка значений ключа он может работать на нескольких потоках, скажем, на два, первый поток, заполняющий первую половину, и второй поток, заполняющий вторую половину параллельно. Это испортит ord1[0] += 1 трюк. Но я сомневаюсь, что он делает такую ​​оптимизацию, поскольку это просто кажется излишним.

Но для устранения каких-либо сомнений мы можем следовать этой альтернативной стратегии реализации самостоятельно, хотя решение становится немного более подробным:

 def order_sublist(items): """ >>> order_sublist(['A', 'B2', 'B11', 'B22', 'B', 'B1', 'B21', 'C', 'C1', 'C11', 'C2']) ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] >>> order_sublist(['X', 'B2', 'B11', 'B22', 'B', 'B1', 'B21', 'C', 'Q1', 'C11', 'C2']) ['X', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'Q1', 'C11', 'C2'] """ ord1 = 0 zipped = [] for item in items: if not item.startswith('B'): ord1 += 1 zipped.append((ord1, item)) def key(item): if not item[1].startswith('B'): return item[0], return item[0], int(item[1][1:] or 0) return [v for _, v in sorted(zipped, key=key)] 

Обратите внимание, что благодаря доктринам у нас есть простой способ проверить, что альтернативная реализация по-прежнему работает по-прежнему.


Что, если вам нужен этот список примеров:

 ['X', 'B', 'B1', 'B11', 'B2', 'B22', 'C', 'Q1', 'C11', 'C2', 'B21'] 

Чтобы отсортироваться следующим образом:

 ['X', 'B', 'B1', 'B2', 'B11', 'B21', 'C', 'Q1', 'C11', 'C2', 'B22'] 

То есть элементы, начинающиеся с «B», отсортированные по их числовому значению, даже если они не образуют непрерывную подпоследовательность ?

Это невозможно с помощью магической функции. Это, конечно, возможно, хотя, с некоторыми более legwork. Ты мог:

  1. Создайте список с исходными индексами элементов, начинающихся с «B»
  2. Создайте список с элементами, начинающимися с «B», и отсортируйте их любым способом
  3. Записать содержимое отсортированного списка в исходные индексы

Если вам нужна помощь в этой последней реализации, дайте мне знать.

Большинство ответов были сосредоточены на B, в то время как мне было необходимо более общее решение, как было отмечено. Вот один из них:

 def _order_by_number(items): regex = re.compile(u'(.*?)(\d*)$') # pass a regex in for generality keys = {k: regex.match(k) for k in items} keys = {k: (v.groups()[0], int(v.groups()[1] or 0)) for k, v in keys.iteritems()} items.sort(key=keys.__getitem__) 

Я все еще ищу волшебный ключ, однако это оставило бы на месте

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

 >>> from natsort import natsorted >>> >>> a = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] >>> natsorted(a) ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11'] 

Если элементы, которые должны быть отсортированы, смежны друг с другом в списке:

Вы можете использовать cmp в sorted() -функции вместо key :

 s1=['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] def compare(a,b): if (a[0],b[0])==('B','B'): #change to whichever condition you'd like inta=int(a[1:] or 0) intb=int(b[1:] or 0) return cmp(inta,intb) #change to whichever mode of comparison you'd like else: return 0 #if one of a, b doesn't fulfill the condition, do nothing sorted(s1,cmp=compare) 

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

Если элементы, которые должны быть отсортированы, не все смежные друг с другом в списке:

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

 s1=['11', '2', 'A', 'B', 'B11', 'B21', 'B1', 'B2', 'C', 'C11', 'C2', 'B09','C8','B19'] def cond1(a): #change this to whichever condition you'd like return a[0]=='B' def comparison(a,b): #change this to whichever type of comparison you'd like to make inta=int(a[1:] or 0) intb=int(b[1:] or 0) return cmp(inta,intb) def n2CompareSort(alist,condition,comparison): for i in xrange(len(alist)): for j in xrange(i): if condition(alist[i]) and condition(alist[j]): if comparison(alist[i],alist[j])==-1: alist[i], alist[j] = alist[j], alist[i] #in-place swap n2CompareSort(s1,cond1,comparison) 

Я не думаю, что любая из этих проблем представляет собой сложность, чем создание отдельного списка / кортежа, но она «на месте» и оставляет элементы, которые не соответствуют нашему состоянию нетронутыми.

Вы можете использовать следующую ключевую функцию. Он вернет кортеж формы (letter, number) если есть число, или формы (letter,) если числа нет. Это работает с ('A',) < ('A', 1) .

 import re a = ['A', 'B' ,'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] regex = re.compile(r'(\d+)') def order(e): num = regex.findall(e) if num: num = int(num[0]) return e[0], num return e, print(sorted(a, key=order)) >> ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11'] 

Если я понимаю ваш вопрос ясно, вы пытаетесь отсортировать массив по двум атрибутам ; алфавит и конечное «число» .

Вы могли бы просто сделать что-то вроде

 data = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] data.sort(key=lambda elem: (elem[0], int(elem[1:])) 

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

 def sortKey(elem): try: attribute = (elem[0], int(elem[1:])) except: attribute = (elem[0], 0) return attribute 

С помощью этой ключевой функции сортировки мы можем сортировать элемент на месте

 data.sort(key=sortKey) 

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

Чтобы ответить именно на то, что вы описали, вы можете сделать это:

 l = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2', 'D'] def custom_sort(data, c): s = next(i for i, x in enumerate(data) if x.startswith(c)) e = next((i for i, x in enumerate(data) if not x.startswith(c) and i > s), -1) return data[:s] + sorted(data[s:e], key=lambda d: int(d[1:] or -1)) + data[e:] print(custom_sort(l, "B")) 

если вы, какой полный вид, можете просто сделать это (как @Mike JS Choi ответил, но попроще):

 output = sorted(l, key=lambda elem: (elem[0], int(elem[1:] or -1))) 

Вы можете использовать ord () для преобразования для примера «B11» в числовом значении:

 cells = ['B11', 'C1', 'A', 'B1', 'B2', 'B21', 'B22', 'C11', 'C2', 'B'] conv_cells = [] ## Transform expression in numerical value. for x, cell in enumerate(cells): val = ord(cell[0]) * (ord(cell[0]) - 65) ## Add weight to ensure respect order. if len(cell) > 1: val += int(cell[1:]) conv_cells.append((val, x)) ## List of tuple (num_val, index). ## Display result. for x in sorted(conv_cells): print(str(cells[x[1]]) + ' - ' + str(x[0])) 

Если вы хотите сортировать разные правила для разных подгрупп, вы можете использовать кортежи в качестве сортировочных ключей. В этом случае элементы будут сгруппированы и отсортированы по слоям: сначала с помощью элемента первого кортежа, затем в каждой подгруппе вторым элементом кортежа и т. Д. Это позволяет нам иметь разные правила сортировки в разных подгруппах. Единственный предел – предметы должны быть сопоставимы в каждой группе. Например, вы не можете иметь ключи типа int и str в одной и той же подгруппе, но вы можете иметь их в разных подгруппах.

Попробуем применить его к задаче. Мы подготовим кортежи с типами элементов ( str , int ) для элементов B и кортежей с ( str , str ) для всех остальных.

 def sorter(elem): letter, num = elem[0], elem[1:] if letter == 'B': return letter, int(num or 0) # hack - if we've got `''` as num, replace it with `0` else: return letter, num data = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] sorted(data, key=sorter) # returns ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] 

ОБНОВИТЬ

Если вы предпочитаете его в одной строке:

 data = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] sorted(data, key=lambda elem: (elem[0], int(elem[1:] or 0) if elem[0]=='B' else elem[:1] # result ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11'] 

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

 import numpy as np def sort_with_prefix(list, prefix): alist = np.array(list) ix = np.where([l.startswith(prefix) for l in list]) alist[ix] = [prefix + str(n or '') for n in np.sort([int(l.split(prefix)[-1] or 0) for l in alist[ix]])] return alist.tolist() 

Например:

 l = ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11'] print(sort_with_prefix(l, 'B')) >> ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] 

Используя только ключ и предварительное условие, что последовательность уже «отсортирована»:

 import re s = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] def subgroup_ordinate(element): # Split the sequence element values into groups and ordinal values. # use a simple regex and int() in this case m = re.search('(B)(.+)', element) if m: subgroup = m.group(1) ordinate = int(m.group(2)) else: subgroup = element ordinate = None return (subgroup, ordinate) print sorted(s, key=subgroup_ordinate) ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11'] 

Функция subgroup_ordinate() выполняет две функции: идентифицирует группы для сортировки, а также определяет порядковый номер внутри групп. В этом примере используется регулярное выражение, но функция может быть произвольно сложной. Например, мы можем изменить его на ur'(B|C)(.+)' И отсортировать последовательности B и C.

 ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11'] 

Внимательно прочитав вопрос о щедрости, я отмечаю, что требование «сортирует некоторые значения, оставляя других» на месте ». Определение функции сравнения для возврата 0 для элементов, которые не входят в подгруппы, оставит эти элементы там, где они были в последовательности.

 s2 = ['X', 'B', 'B1', 'B2', 'B11', 'B21', 'A', 'C', 'C1', 'C2', 'C11'] def compare((_a,a),(_b,b)): return 0 if a is None or b is None else cmp(a,b) print sorted(s, compare, subgroup_ordinate) ['X', 'B', 'B1', 'B2', 'B11', 'B21', 'A', 'C', 'C1', 'C2', 'C11'] 
 import re from collections import OrderedDict a = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2'] dict = OrderedDict() def get_str(item): _str = list(map(str, re.findall(r"[A-Za-z]", item))) return _str def get_digit(item): _digit = list(map(int, re.findall(r"\d+", item))) return _digit for item in a: _str = get_str(item) dict[_str[0]] = sorted([get_digit(dig) for dig in a if _str[0] in dig]) nested_result = [[("{0}{1}".format(k,v[0]) if v else k) for v in dict[k]] for k in dict.keys()] print (nested_result) # >>> [['A'], ['B', 'B1', 'B2', 'B11', 'B21', 'B22'], ['C', 'C1', 'C2', 'C11']] result = [] for k in dict.keys(): for v in dict[k]: result.append("{0}{1}".format(k,v[0]) if v else k) print (result) # >>> ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11'] 

Если вы хотите отсортировать произвольное подмножество элементов, оставив другие элементы на месте, может быть полезно создать представление над исходным списком. Идея взгляда в целом заключается в том, что он похож на объектив над исходным списком, но его изменение будет манипулировать базовым исходным списком. Рассмотрим этот вспомогательный класс:

 class SubList: def __init__(self, items, predicate): self.items = items self.indexes = [i for i in range(len(items)) if predicate(items[i])] @property def values(self): return [self.items[i] for i in self.indexes] def sort(self, key): for i, v in zip(self.indexes, sorted(self.values, key=key)): self.items[i] = v 

Конструктор сохраняет исходный список в self.items и исходные индексы в self.indexes , как определено predicate . В ваших примерах функция predicate может быть такой:

 def predicate(item): return item.startswith('B') 

Затем свойство values является объективом над исходным списком, возвращая список значений, выбранных из исходного списка исходными индексами.

Наконец, функция sort использует self.values для сортировки, а затем изменяет исходный список.

Рассмотрите эту демонстрацию с помощью доктрин:

 def demo(values): """ >>> demo(['X', 'b3', 'a', 'b1', 'b2']) ['X', 'b1', 'a', 'b2', 'b3'] """ def predicate(item): return item.startswith('b') sub = SubList(values, predicate) def key(item): return int(item[1:]) sub.sort(key) return values 

Обратите внимание, что SubList используется только как инструмент для управления входными values . После вызова sub.sort values изменяются, элементы сортируются по функции predicate и сортируются в соответствии с key функцией, а все остальные элементы никогда не перемещаются.

Используя этот SubList помощник SubList с соответствующими predicate и key функциями, вы можете сортировать произвольный выбор элементов списка.

 def compound_sort(input_list, natural_sort_prefixes=()): padding = '{:0>%s}' % len(max(input_list, key=len)) return sorted( input_list, key = lambda li: \ ''.join( [li for c in '_' if not li.startswith(natural_sort_prefixes)] or [c for c in li if not c.isdigit()] + \ [c for c in padding.format(li) if c.isdigit()] ) ) 

Этот метод сортировки получает:

  • input_list : list который нужно отсортировать,
  • natural_sort_prefixes : string или tuple string s.

Элементы списка, нацеленные на natural_sort_prefixes будут отсортированы естественным образом . Элементы, не соответствующие этим префиксам, будут отсортированы лексикографически .

Этот метод предполагает, что элементы списка структурированы как один или несколько нечисловых символов, за которыми следуют одна или несколько цифр.

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

Вы можете использовать его так:

 print compound_sort(['A', 'B' , 'B11', 'B1', 'B2', 'C11', 'C2'], natural_sort_prefixes=("A","B")) # ['A', 'B', 'B1', 'B2', 'B11', 'C11', 'C2'] 
  • Python и разрывы строк
  • Как форматировать вывод печати в фиксированную ширину?
  • Ошибка: не может умножить последовательность на non-int типа «Mul»
  • Грамматический список Присоединиться к Python
  • Как получить все смежные подстроки строки в Python?
  • Печать СПИСОК символов unicode без символов escape
  • Объединить переименование словаря Python в список словарей
  • Список фильтров python на основе ключевых значений
  • Python - лучший язык программирования в мире.