Разбиение списка списков на несколько списков по длине подсписка - PullRequest
1 голос
/ 18 марта 2020

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

tokens = [[46565], [44460], [73, 2062], [1616, 338], [9424, 24899], [1820, 11268], [43533, 5356], [9930, 1053], [260, 259, 1151], [83, 31840, 292, 3826]]

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

a = [[46565], [44460]]
b = [[73, 2062], [1616, 338], [9424, 24899], [1820, 11268], [43533, 5356], [9930, 1053]]
c = [[260, 259, 1151]]
d = [[83, 31840, 292, 3826]]

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

Я подумал, что, возможно, я мог бы что-то сделать с:

lengths = list(map(len,tokens))

for k, v in zip(lengths, tokens):
    <SOME CODE HERE>

Есть идеи?

Ответы [ 3 ]

3 голосов
/ 18 марта 2020

В одну сторону sorted с itertools.groupby:

[list(v) for _ ,v in groupby(sorted(tokens, key=len), key=len)]

[[[46565], [44460]],
 [[73, 2062],
  [1616, 338],
  [9424, 24899],
  [1820, 11268],
  [43533, 5356],
  [9930, 1053]],
 [[260, 259, 1151]],
 [[83, 31840, 292, 3826]]]
2 голосов
/ 18 марта 2020

Это настолько эффективно, насколько это возможно, и довольно просто:

tokens = [
    [46565], [44460], [73, 2062], [1616, 338], 
    [9424, 24899], [1820, 11268], [43533, 5356], 
    [9930, 1053], [260, 259, 1151], 
    [83, 31840, 292, 3826]
]
groups = {}
for sublist in tokens:
    groups.setdefault(len(sublist), []).append(sublist)

После этого groups будет словарем с ключами для длины подсписка и значениями, которые все подсписки этой длины, в том порядке, в котором они были найдены в tokens. Затем вы можете назначить эти записи именованным переменным, если хотите (a = groups[1], et c.), Но для большинства рабочих процессов вам будет лучше работать напрямую со словарем groups, поскольку это обобщает решение (Что если есть список длиной 0? Как насчет списка из 15 элементов?).

Нет способа сделать это с пониманием списка из одной строки, потому что вам нужно кластеризовать каждое входное значение по-разному. Для агрегации (как это), лучшее решение - почти всегда запускать for l oop поверх ваших входных данных и создавать или обновлять записи в выходном словаре.

Метод словарей .setdefault также очень полезен для этого шаблона, поскольку он избавляет вас от необходимости проверять, существует ли запись, прежде чем обновлять ее. Кроме того, вы можете использовать groups = collections.defaultdict(list), а затем просто обновить его с помощью groups[len(sublist)].append(sublist).

0 голосов
/ 18 марта 2020

Не самый оптимальный способ сделать это, но следует за большинством из этого.

import string

alphabets = string.ascii_lowercase

tokens = [[46565], [44460], [73, 2062], [1616, 338], [9424, 24899], [1820, 11268], [43533, 5356], [9930, 1053], [260, 259, 1151], [83, 31840, 292, 3826]]

numbering = {(ord(k)-96):k for k in alphabets}
output = {k:[] for k in alphabets}


lengths = list(map(len,tokens))

for k, v in zip(lengths, tokens):
    output[numbering[k]].append(v)

print(output)

Это вывод:

{'a': [[46565], [44460]], 'b': [[73, 2062], [1616, 338], [9424, 24899], [1820, 11268], [43533, 5356], [9930, 1053]], 'c': [[260, 259, 1151]], 'd': [[83, 31840, 292, 3826]], 'e': [], 'f': [], 'g': [], 'h': [], 'i': [], 'j': [], 'k': [], 'l': [], 'm': [], 'n': [], 'o': [], 'p': [], 'q': [], 'r': [], 's': [], 't': [], 'u': [], 'v': [], 'w': [], 'x': [], 'y': [], 'z': []}
...