Как сделать плоский список из списка списков - PullRequest
2641 голосов
/ 05 июня 2009

Интересно, есть ли ярлык для создания простого списка из списка списков в Python.

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

Код

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

Сообщение об ошибке

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'

Ответы [ 39 ]

5 голосов
/ 20 июня 2018
flat_list = []
for i in list_of_list:
    flat_list+=i

Этот код также отлично работает, так как он просто расширяет список полностью. Хотя это очень похоже, но есть только один цикл. Так что это сложнее, чем добавление 2 для циклов.

5 голосов
/ 09 января 2018

Еще один необычный подход, который работает для гетеро- и однородных списков целых чисел:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]
5 голосов
/ 08 августа 2017
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
5 голосов
/ 25 марта 2017

Простой код для underscore.py корпус вентилятора

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Это решает все проблемы сглаживания (ни один элемент списка или сложное вложение)

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Вы можете установить underscore.py с пунктами

pip install underscore.py
4 голосов
/ 20 сентября 2018

Простой рекурсивный метод с использованием reduce из functools и оператора add в списках:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Функция flatten принимает lst в качестве параметра. Он зацикливает все элементы lst до достижения целых чисел (также может изменить int на float, str и т. Д. Для других типов данных), которые добавляются к возвращаемому значению самой внешней рекурсии. *

Рекурсия, в отличие от таких методов, как for циклы и монады, заключается в том, что это общее решение, не ограниченное глубиной списка . Например, список с глубиной 5 может быть сведен так же, как l:

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]
4 голосов
/ 16 мая 2018

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

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

Выход

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

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

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

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

Выход снова

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Хотя в настоящее время я не уверен в эффективности.

4 голосов
/ 01 февраля 2018

Примечание : ниже относится к Python 3.3+, потому что он использует yield_from. six также является сторонним пакетом, хотя и стабильным. В качестве альтернативы вы можете использовать sys.version.


В случае obj = [[1, 2,], [3, 4], [5, 6]] все решения здесь хороши, включая понимание списка и itertools.chain.from_iterable.

Однако рассмотрим этот немного более сложный случай:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Здесь есть несколько проблем:

  • Один элемент, 6, это просто скаляр; это не итерация, поэтому приведенные выше маршруты не будут выполнены.
  • Один элемент, 'abc', является технически повторяемым (все str с). Однако, читая между строк немного, вы не хотите рассматривать это как таковое - вы хотите рассматривать это как отдельный элемент.
  • Последний элемент, [8, [9, 10]], сам по себе является вложенным итерируемым. Базовое понимание списка и chain.from_iterable извлекает только «1 уровень вниз».

Вы можете исправить это следующим образом:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Здесь вы проверяете, что подэлемент (1) итеративен с Iterable, ABC из itertools, но также хотите убедиться, что (2) элемент равен не «строковый».

3 голосов
/ 27 октября 2016

Если вы готовы отказаться от небольшого количества скорости для более чистого взгляда, тогда вы можете использовать numpy.concatenate().tolist() или numpy.concatenate().ravel().tolist():

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

Вы можете узнать больше здесь в документации numpy.concatenate и numpy.ravel

3 голосов
/ 29 ноября 2016

Самое быстрое решение, которое я нашел (в любом случае для большого списка):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

Готово! Конечно, вы можете превратить его обратно в список, выполнив list (l)

3 голосов
/ 21 сентября 2017

Недавно я столкнулся с ситуацией, когда в подсписках, таких как

, было сочетание строк и числовых данных.
test = ['591212948',
['special', 'assoc', 'of', 'Chicago', 'Jon', 'Doe'],
['Jon'],
['Doe'],
['fl'],
92001,
555555555,
'hello',
['hello2', 'a'],
'b',
['hello33', ['z', 'w'], 'b']]

, где методы типа flat_list = [item for sublist in test for item in sublist] не работали. Итак, я придумал следующее решение для 1+ уровня сублистов

def concatList(data):
    results = []
    for rec in data:
        if type(rec) == list:
            results += rec
            results = concatList(results)
        else:
            results.append(rec)
    return results

И результат

In [38]: concatList(test)
Out[38]:
 Out[60]:
['591212948',
'special',
'assoc',
'of',
'Chicago',
'Jon',
'Doe',
'Jon',
'Doe',
'fl',
92001,
555555555,
'hello',
'hello2',
'a',
'b',
'hello33',
'z',
'w',
'b']
...