Сгладить неправильный список списков - PullRequest
397 голосов
/ 29 января 2010

Да, я знаю, что этот предмет уже освещался ( здесь , здесь , здесь , здесь ), но насколько как я знаю, все решения, кроме одного, терпят неудачу в списке, подобном этому:

L = [[[1, 2, 3], [4, 5]], 6]

Где желаемый результат

[1, 2, 3, 4, 5, 6]

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

def flatten(x):
    result = []
    for el in x:
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

flatten(L)

Это лучшая модель? Я что-то упустил? Есть проблемы?

Ответы [ 41 ]

0 голосов
/ 20 июля 2017

Итеративное решение с Python 3

Это решение может работать со всеми объектами, кроме str и байтов.

from collections import Iterable
from collections import Iterator


def flat_iter(obj):
    stack = [obj]
    while stack:
        element = stack.pop()
        if element and isinstance(element, Iterator):
            stack.append(element)
            try:
                stack.append(next(element))
            except StopIteration:
                stack.pop()
        elif isinstance(element, Iterable) and not isinstance(element, (str, bytes)):
            stack.append(iter(element))
        else:
            yield element


tree_list = [[(1,2,3),(4,5,6, (7,8, 'next element is 5')), (5,6), [[[3,4,5],'foo1'],'foo2'],'foo3']]

not_iterable = 10

it1 = flat_iter(tree_list)
it2 = flat_iter(not_iterable)

print(list(it1))
print(list(it2))

[1, 2, 3, 4, 5, 6, 7, 8, «следующим элементом является 5», 5, 6, 3, 4, 5, «foo1», «foo2», «foo3»]

[10]

0 голосов
/ 22 января 2019

Нет рекурсии или вложенных циклов. Несколько строк. Хорошо отформатирован и легко читается:

def flatten_deep(arr: list):
""" Flattens arbitrarily-nested list `arr` into single-dimensional. """

while arr:
    if isinstance(arr[0], list):  # Checks whether first element is a list
        arr = arr[0] + arr[1:]  # If so, flattens that first element one level
    else:
        yield arr.pop(0)  # Otherwise yield as part of the flat array

flatten_deep(L)

Из моего собственного кода на https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py

0 голосов
/ 21 декабря 2010

Я новичок в Python и пришел с фоном LISP. Вот что я придумал (посмотрите названия вар для lulz):

def flatten(lst):
    if lst:
        car,*cdr=lst
        if isinstance(car,(list,tuple)):
            if cdr: return flatten(car) + flatten(cdr)
            return flatten(car)
        if cdr: return [car] + flatten(cdr)
        return [car]

Кажется, работает. Тест:

flatten((1,2,3,(4,5,6,(7,8,(((1,2)))))))

возвращается:

[1, 2, 3, 4, 5, 6, 7, 8, 1, 2]
0 голосов
/ 04 апреля 2016

Я тупой парень, поэтому я дам "тупое" решение. Вся эта рекурсия ранит мой мозг.

flattened_list = []
nested_list = [[[1, 2, 3], [4, 5]], 6]

def flatten(nested_list, container):
    for item in nested_list:
        if isintance(item, list):
            flatten(item)
        else:
            container.append(i)

>>> flatten(nested_list, flattened_list)
>>> flattened_list
[1, 2, 3, 4, 5, 6]

Я понимаю, что он использует побочный эффект, но, насколько я понимаю, рекурсия может пойти

0 голосов
/ 22 марта 2016

Мы также можем использовать функцию «type» в python. При переборе списка мы проверяем, является ли элемент списком или нет. Если нет, мы «добавляем» его, иначе «расширяем» его. Вот пример кода -

l=[1,2,[3,4],5,[6,7,8]]
x=[]
for i in l:
    if type(i) is list:
        x.extend(i)
    else:
        x.append(i)
print x

Выход:

[1, 2, 3, 4, 5, 6, 7, 8]

Для получения дополнительной информации о append () и extend () посетите этот веб-сайт: https://docs.python.org/2/tutorial/datastructures.html

0 голосов
/ 12 августа 2011

Я не вижу ничего подобного, опубликованного здесь и только что пришедшего сюда из закрытого вопроса на ту же тему, но почему бы просто не сделать что-то подобное (если вы знаете тип списка, который вы хотите разделить):

>>> a = [1, 2, 3, 5, 10, [1, 25, 11, [1, 0]]]    
>>> g = str(a).replace('[', '').replace(']', '')    
>>> b = [int(x) for x in g.split(',') if x.strip()]

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

0 голосов
/ 14 октября 2015

Бесстыдно взято из моего собственного ответа на другой вопрос .

Эта функция

  • Не использует isinstance, потому что это зло и нарушает печать утки.
  • Использует reduce рекурсивно. Должен быть ответ, используя reduce.
  • Работает с произвольными вложенными списками, элементами которых являются либо вложенные списки, либо не вложенные списки атомов или атомов (с ограничением рекурсии).
  • Не LBYL.
  • Но не с вложенными списками, которые содержат строки в качестве атомов.

код ниже:

def flattener(left, right):
    try:
        res = reduce(flattener, right, left)
    except TypeError:
        left.append(right)
        res = left
    return res


def flatten(seq):
    return reduce(flattener, seq, [])


>>> nested_list = [0, [1], [[[[2]]]],
                   [3, [], [4, 5]],
                   [6, [7, 8],
                    9, [[[]], 10,
                        []]],
                   11, [], [],
                   [12]]
>>> flatten(nested_list)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
0 голосов
/ 27 февраля 2017

Это сгладит список или словарь (или список списков или словарей словарей и т. Д.). Предполагается, что значения являются строками, и создается строка, которая объединяет каждый элемент с аргументом-разделителем. Если вы хотите, вы можете использовать разделитель, чтобы потом разделить результат на объект списка. Он использует рекурсию, если следующим значением является список или строка. Используйте аргумент key, чтобы указать, хотите ли вы получить ключи или значения (установите для ключа значение false) из объекта словаря.

def flatten_obj(n_obj, key=True, my_sep=''):
    my_string = ''
    if type(n_obj) == list:
        for val in n_obj:
            my_sep_setter = my_sep if my_string != '' else ''
            if type(val) == list or type(val) == dict:
                my_string += my_sep_setter + flatten_obj(val, key, my_sep)
            else:
                my_string += my_sep_setter + val
    elif type(n_obj) == dict:
        for k, v in n_obj.items():
            my_sep_setter = my_sep if my_string != '' else ''
            d_val = k if key else v
            if type(v) == list or type(v) == dict:
                my_string += my_sep_setter + flatten_obj(v, key, my_sep)
            else:
                my_string += my_sep_setter + d_val
    elif type(n_obj) == str:
        my_sep_setter = my_sep if my_string != '' else ''
        my_string += my_sep_setter + n_obj
        return my_string
    return my_string

print(flatten_obj(['just', 'a', ['test', 'to', 'try'], 'right', 'now', ['or', 'later', 'today'],
                [{'dictionary_test': 'test'}, {'dictionary_test_two': 'later_today'}, 'my power is 9000']], my_sep=', ')

Выходы:

just, a, test, to, try, right, now, or, later, today, dictionary_test, dictionary_test_two, my power is 9000
0 голосов
/ 27 февраля 2017

Python-3

from collections import Iterable

L = [[[1, 2, 3], [4, 5]], 6,[7,[8,9,[10]]]]

def flatten(thing):
    result = []

    if isinstance(thing, Iterable):
        for item in thing:
            result.extend(flatten(item))
    else:
        result.append(thing)

    return result


flat = flatten(L)
print(flat)
0 голосов
/ 07 декабря 2014

Без использования какой-либо библиотеки:

def flat(l):
    def _flat(l, r):    
        if type(l) is not list:
            r.append(l)
        else:
            for i in l:
                r = r + flat(i)
        return r
    return _flat(l, [])



# example
test = [[1], [[2]], [3], [['a','b','c'] , [['z','x','y']], ['d','f','g']], 4]    
print flat(test) # prints [1, 2, 3, 'a', 'b', 'c', 'z', 'x', 'y', 'd', 'f', 'g', 4]
...