Как разделить отсортированный список по длине элемента

#python #list #sorting #split

#python #Список #сортировка #разделить

Вопрос:

У меня есть список списков, и он отсортирован по длине дочерних списков. например

 [[str], [str1, str2], [str1, str2], [str1, str2, str3], [str1, str2, str3],...]
  

Я хотел бы разделить этот список на подсписки, которые содержат только дочерние элементы одинаковой длины.
например

 [[[str], [str], [str]],  [[str1, str2], [str1, str2], [str1, str2]], ...]
  

Мне было интересно, есть ли более эффективный подход, чем мой, приведенный ниже, с, надеюсь, немного меньшим количеством кода.

 child_list = []
new_list = []
old_list = [['e3510000'], ['e2512001'], ['e3510000'], ['e92d4010'],
            ['e3a0b000', 'e3a0e000'], ['e92d4030', 'e59f5054'],
            ['e59f3038', 'e3530000'], ['e1a0c00d', 'e92dd800']]

# length of child
length = 1
for idx, i in enumerate(old_list):
    if idx == len(old_list)-1:
        child_list.append(i)
        new_list.append(child_list.copy())
    elif length == len(i):
        child_list.append(i)
    elif length < len(i):
        new_list.append(child_list.copy())
        del child_list[:]
        child_list.append(i)
        length = len(i)
  

Вывод:

 [[['e3510000'], ['e2512001'], ['e3510000'], ['e92d4010']],
 [['e3a0b000', 'e3a0e000'], ['e92d4030', 'e59f5054'], 
  ['e59f3038', 'e3530000'], ['e1a0c00d', 'e92dd800']]]
  

Ответ №1:

Вы можете использовать itertools.groupby для группировки списков old по длине. Обратите внимание, что если исходные списки уже упорядочены по их длине, как в вашем примере, сортировка здесь не требуется.

 from itertools import groupby
[list(v) for k,v in groupby(sorted(old_list, key=len), key=len)]
  

Вывод

 [[['e3510000'], ['e2512001'], ['e3510000'], ['e92d4010']],
 [['e3a0b000', 'e3a0e000'],
  ['e92d4030', 'e59f5054'],
  ['e59f3038', 'e3530000'],
  ['e1a0c00d', 'e92dd800']]]
  

Комментарии:

1. Миллион благодарностей за помощь! Здорово, как это можно решить с помощью однострочника с помощью itertools.

Ответ №2:

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

 from collections import defaultdict
data = [["str"], ["str1", "str2"], ["str1", "str2"], ["str1", "str2", "str3"], ["str1", "str2", "str3"]]
dct = defaultdict(list)
for el in data:
    dct[len(el)].append(el)

print(dct.values())
  

Out:

 [[['str']],
 [['str1', 'str2'], ['str1', 'str2']],
 [['str1', 'str2', 'str3'], ['str1', 'str2', 'str3']]]
  

Результаты тестирования: использование решения на основе словаря быстрее:

 from itertools import groupby
from collections import defaultdict

data = [["str"], ["str1", "str2"], ["str1", "str2"], ["str1", "str2", "str3"], ["str1", "str2", "str3"]]

def solve_with_groupby(data):
     return [list(v) for k,v in groupby(sorted(data, key=len), key=len)]

def solve_with_dict(data):
    dct = defaultdict(list)
    for el in data:
        dct[len(el)].append(el)
    return dct.values() 
  

Результат:

 In [10]: timeit solve_with_groupby(data)
100000 loops, best of 3: 5.75 µs per loop

In [11]: timeit solve_with_dict(data)
100000 loops, best of 3: 2.56 µs per loop
  

Ответ №3:

Просто используйте groupby :

 >>> l = [[1]*i for i in range(1, 5) for _ in range(3)]
>>> l
[[1], [1], [1], [1, 1], [1, 1], [1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> result = [list(g) for _, g in groupby(l, key=len)]
>>> result
[[[1], [1], [1]], [[1, 1], [1, 1], [1, 1]], [[1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]]
  

В качестве альтернативы, если они не упорядочены, вы можете использовать defaultdict :

 >>> import random
>>> random.shuffle(l)
>>> l
[[1, 1, 1], [1, 1, 1, 1], [1], [1], [1, 1], [1, 1], [1, 1, 1], [1, 1, 1], [1, 1], [1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for e in l:
...     d[len(e)].append(e)
... 
>>> result = list(d.values())
>>> result
[[[1, 1, 1], [1, 1, 1], [1, 1, 1]], [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], [[1], [1], [1]], [[1, 1], [1, 1], [1, 1]]]