Разверните и сгладьте оборванный вложенный список

92
17

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


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


Вот пример. У меня


   [ [ "id1", [["x", "y", "z"], [1, 2]],    ["a", "b", "c"]],
[ "id2", [["x", "y", "z"], [1, 2, 3]], ["a", "b"]],
[ "id3", [["x", "y"], [1, 2, 3]], ["a", "b", "c", ""]] ]

Вывод, который я в конечном итоге хочу, -


   [[ "id1", "x", "y",  z, 1, 2, "", "a", "b", "c", ""],
[ "id2", "x", "y", z, 1, 2, 3, "a", "b", "", ""],
[ "id3", "x", "y", "", 1, 2, 3, "a", "b", "c", ""]]

Однако промежуточный список, подобный этому


   [ [ "id1", [["x", "y", "z"], [1, 2, ""]], ["a", "b", "c", ""]],
[ "id2", [["x", "y", "z"], [1, 2, 3]], ["a", "b", "", ""]],
[ "id3", [["x", "y", ""], [1, 2, 3]], ["a", "b", "c", ""]] ]

который я могу тогда просто сгладить, также будет хорошо.


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


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


   [ [ "id1", [[x, y, z], [1, 2]],             ["a", "b", "c"]],
[ "id2", [[x, y, z], [1, 2, 3]], ["bla"], ["a", "b"]],
[ "id3", [[x, y], [1, 2, 3]], ["a", "b", "c", ""]] ]

который должен стать


   [[ "id1", x, y,  z, 1, 2, "",    "", "a", "b", "c", ""],
[ "id2", x, y, z, 1, 2, 3, "bla", "a", "b", "", ""],
[ "id3", x, y, "", 1, 2, 3, "", "a", "b", "c", ""]]

Спасибо за любые комментарии, и, пожалуйста, извините, если это тривиально, я довольно новичок в Python.

спросил(а) 2012-08-16T12:33:00+04:00 8 лет, 2 месяца назад
1
Решение
106

У меня есть простое решение для случая "той же структуры" с использованием рекурсивного генератора и функции izip_longest от itertools. Этот код предназначен для Python 2, но с несколькими настройками (отмеченными в комментариях) можно заставить работать на Python 3:


from itertools import izip_longest # in py3, this is renamed zip_longest

def flatten(nested_list):
return zip(*_flattengen(nested_list)) # in py3, wrap this in list()

def _flattengen(iterable):
for element in izip_longest(*iterable, fillvalue=""):
if isinstance(element[0], list):
for e in _flattengen(element):
yield e
else:
yield element

В Python 3.3 это станет еще проще благодаря PEP 380, который позволит рекурсивному шагу for e in _flatengen(element): yield e стать yield from _flattengen(element).

ответил(а) 2012-08-20T19:28:00+04:00 8 лет, 2 месяца назад
81

На самом деле нет решения для общего случая, когда структура не является одинаковой.
Например, нормальный алгоритм будет соответствовать ["bla"] с ["a", "b", "c"], и результат будет


 [  [ "id1", x, y,  z, 1, 2, "",   "a", "b", "c", "",  "",  ""],
[ "id2", x, y, z, 1, 2, 3, "bla", "", "", "", "a", "b"],
[ "id3", x, y, "", 1, 2, 3, "a", "b", "c", "", "", ""]]

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


import itertools

def normalize(l):
# just hack the first item to have only lists of lists or lists of items
for sublist in l:
sublist[0] = [sublist[0]]

# break the nesting
def flatten(l):
for item in l:
if not isinstance(item, list) or 0 == len([x for x in item if isinstance(x, list)]):
yield item
else:
for subitem in flatten(item):
yield subitem

l = [list(flatten(i)) for i in l]

# extend all lists to greatest length
list_lengths = { }
for i in range(0, len(l[0])):
for item in l:
list_lengths[i] = max(len(item[i]), list_lengths.get(i, 0))

for i in range(0, len(l[0])):
for item in l:
item[i] += [''] * (list_lengths[i] - len(item[i]))

# flatten each row
return [list(itertools.chain(*sublist)) for sublist in l]

l = [ [ "id1", [["x", "y", "z"], [1, 2]], ["a", "b", "c"]],
[ "id2", [["x", "y", "z"], [1, 2, 3]], ["a", "b"]],
[ "id3", [["x", "y"], [1, 2, 3]], ["a", "b", "c", ""]] ]
l = normalize(l)
print l

ответил(а) 2012-08-20T18:39:00+04:00 8 лет, 2 месяца назад
41

def recursive_pad(l, spacer=""):
# Make the function never modify it arguments.
l = list(l)

is_list = lambda x: isinstance(x, list)
are_subelements_lists = map(is_list, l)
if not any(are_subelements_lists):
return l

# Would catch [[], [], "42"]
if not all(are_subelements_lists) and any(are_subelements_lists):
raise Exception("Cannot mix lists and non-lists!")

lengths = map(len, l)
if max(lengths) == min(lengths):
#We're already done
return l
# Pad it out
map(lambda x: list_pad(x, spacer, max(lengths)), l)
return l

def list_pad(l, spacer, pad_to):
for i in range(len(l), pad_to):
l.append(spacer)

if __name__ == "__main__":
print(recursive_pad([[[[["x", "y", "z"], [1, 2]], ["a", "b", "c"]], [[[x, y, z], [1, 2, 3]], ["a", "b"]], [[["x", "y"], [1, 2, 3]], ["a", "b", "c", ""]] ]))


Изменить: Вообще-то, я неправильно понял ваш вопрос. Этот код решает несколько другую проблему.

ответил(а) 2012-08-16T13:38:00+04:00 8 лет, 2 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема