550

Yes, I know this subject has been covered before:

but as far as I know, all solutions, except for one, fail on a list like [[[1, 2, 3], [4, 5]], 6], where the desired output is [1, 2, 3, 4, 5, 6] (or perhaps even better, an iterator).

The only solution I saw that works for an arbitrary nesting is found in this question:

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

Is this the best approach? Did I overlook something? Any problems?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
telliott99
  • 7,762
  • 4
  • 26
  • 26
  • 31
    The fact that there are this many answers and so much action on this question really suggests that this should be a built-in function somewhere, right? It's especially too bad the compiler.ast was removed from Python 3.0 – Mittenchops Mar 18 '14 at 13:22
  • 3
    I would say that what Python really needs is unbroken recursion rather than another builtin. – clay Apr 07 '15 at 23:27
  • 4
    @Mittenchops: totally disagree, the fact that people working with obviously bad APIs/overly complicated data structures (just a note: `list`s intended to be homogeneous) doesn't mean it's a Python's fault and we need a builtin for such task – Azat Ibrakov May 23 '19 at 09:12
  • 6
    If you can afford adding a package to your project - I suppose the [more_itertools.collapse](https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse) solution will do it best. From this answer: https://stackoverflow.com/a/40938883/3844376 – viddik13 Mar 05 '20 at 15:57
  • @viddik13: please consider making that an answer for this question, as well. It would absolutely get my upvote. (I agree with Mittenchops.) The fact that it's not a _built-in_ function is fine (re Azat Ibrakov), but there should be (and, apparently, is!) a library routine for doing this. (Because: not all _irregularity_ is "bad"/"overly complicated". Sometimes, it's just... not _regular_, and that's OK. IMHO. As long as what it _is_ is well defined, and it can be, and still be irregular ("an arbitrarily nested list (of lists (of lists...)) of integers", for example, is well defined).) – lindes Feb 07 '21 at 19:06
  • Use a recursive function to walk the list of list tree https://stackabuse.com/python-how-to-flatten-list-of-lists/ – Golden Lion May 26 '21 at 21:58

51 Answers51

470

Using generator functions can make your example easier to read and improve performance.

Python 2

Using the Iterable ABC added in 2.6:

from collections import Iterable

def flatten(xs):
    for x in xs:
        if isinstance(x, Iterable) and not isinstance(x, basestring):
            for item in flatten(x):
                yield item
        else:
            yield x

Python 3

In Python 3, basestring is no more, but the tuple (str, bytes) gives the same effect. Also, the yield from operator returns an item from a generator one at a time.

from collections.abc import Iterable

def flatten(xs):
    for x in xs:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Cristian
  • 42,563
  • 25
  • 88
  • 99
  • 9
    Of all the suggestions on this page, this is the only one that flattened this list `l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))` in a snap when i did this `list(flatten(l))`. All the others, would start working and take forever! – JWL Jun 07 '12 at 15:04
  • 8
    This also flattens dictionaries. Maybe you want to use `collections.Sequence` instead of `collections.Iteratable`? – josch Jun 21 '15 at 12:37
  • 3
    This doesn't work with things that aren't lists initially, e.g. `for i in flatten(42): print (i)`. This could be fixed by moving the `isinstance`-test and the else-clause outside of the `for el`-loop. (Then you could throw anything at it, and it would make a flattened list out of it) – RolKau Aug 18 '15 at 11:17
  • 2
    Why do you need the compound check `if isinstance(el, collections.Iterable) and not isinstance(el, basestring)`? Why not just `if not isinstance(el, list)`? – yelsayed May 09 '16 at 04:41
  • 6
    For Python 3.7, using `collections.Iterable` is deprecated. Use `collections.abc.Iterable` instead. – dawg Sep 30 '18 at 15:53
  • 2
    @yelsayed because you want to prevent separating strings into their individual letters. – gosuto Dec 30 '18 at 09:26
  • No recursion needed. – Jorge Orpinel Pérez Jan 22 '19 at 10:01
  • 5
    Indeed, recursion is **never** needed. In this specific case using recursion is not the best solution as it will crash on deeply nested lists (depth > 1000). But if you don't aim at having something safe, then yes recursive function are better as they are way easier to read/write. – cglacet Mar 11 '19 at 09:13
  • I've recently started getting this warning with this: `DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working` – Max Ghenis Nov 19 '19 at 06:04
  • 1
    Could somebody walk me through what's happening here? It goes a bit over my head – jbuddy_13 Apr 26 '20 at 13:38
  • @jbuddy_13 `yield from ` is exactly the same as `for x in : yield x`. It's basically a one liner syntax for a for-loop generator. So what `flatten()` is doing is it's checking for either one of two cases. The base case (in the `else`) is that the element is not an iterable, so yield that element. The recursive case (in the `if`) is that the element is an iterable, so pass it back into `flatten()` and yield whatever the results are. – Nevermore Jan 01 '22 at 17:19
  • @gosuto, gist link is broken as of 2023 Jan 3. Is there a relocated version? – user3897315 Jan 03 '23 at 23:48
  • @user3897315 i put a concise version of this in a Gist for further reference: https://gist.github.com/gosuto-inzasheru/b6deccd3fd5fefbabb72759c74040745 – gosuto Jan 04 '23 at 10:53
67

My solution:

import collections


def flatten(x):
    if isinstance(x, collections.Iterable):
        return [a for i in x for a in flatten(i)]
    else:
        return [x]

A little more concise, but pretty much the same.

Georgy
  • 12,464
  • 7
  • 65
  • 73
Josh Lee
  • 171,072
  • 38
  • 269
  • 275
  • 7
    You can do this without importing anything if you just `try: iter(x)` to test whether it's iterable… But I don't think having to import a stdlib module is a downside worth avoiding. – abarnert Jul 05 '13 at 08:03
  • 8
    Worth to note that this solution works only if all the items are of type `int` – Nir Alfasi Dec 07 '14 at 06:14
  • 3
    Could make it more concise, `def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]` - but readability might be subjective here. – Zero Jul 25 '17 at 17:13
  • 5
    this doesn't work on strings because strings are iterable too. Replace the condition with `if isinstance(x, collections.Iterable) and not isinstance(x, basestring)` – aandis Nov 29 '18 at 11:51
  • replace `collections.Iterable` with `list` – noobninja Jun 17 '20 at 01:59
  • RecursionError: maximum recursion depth exceeded in comparison – Eduardo Luis Santos May 03 '21 at 15:45
  • `Python3` Update: `basestring` is python2 this doesn't work on strings because strings are iterable too. Replace the condition with `if isinstance(x, collections.Iterable) and not isinstance(x, str):` – Rolf of Saxony Nov 23 '22 at 13:15
49

Generator using recursion and duck typing (updated for Python 3):

def flatten(L):
    for item in L:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

list(flatten([[[1, 2, 3], [4, 5]], 6]))
>>>[1, 2, 3, 4, 5, 6]
dansalmo
  • 11,506
  • 5
  • 58
  • 53
45

Here is my functional version of recursive flatten which handles both tuples and lists, and lets you throw in any mix of positional arguments. Returns a generator which produces the entire sequence in order, arg by arg:

flatten = lambda *n: (e for a in n
    for e in (flatten(*a) if isinstance(a, (tuple, list)) else (a,)))

Usage:

l1 = ['a', ['b', ('c', 'd')]]
l2 = [0, 1, (2, 3), [[4, 5, (6, 7, (8,), [9]), 10]], (11,)]
print list(flatten(l1, -2, -1, l2))
['a', 'b', 'c', 'd', -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
samplebias
  • 37,113
  • 6
  • 107
  • 103
  • 3
    great solution, however would be much helpful if you added some comment to describe what `e`, `a`, `n` refer to – Kristof Pal Nov 20 '13 at 14:16
  • 3
    @WolfgangKuehne: Try `args` for `n`, `intermediate` (or the shorter `mid` or you might prefer `element`) for `a` and `result` for `e`, so: `flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))` – Dennis Williamson Jan 05 '14 at 21:18
  • This is significantly faster than `compiler.ast.flatten`. Great, compact code, works for any (I think) object type. – bcdan Jul 30 '15 at 23:04
  • This is the only solution I have come across, in a moderate google search, on any website that actually works for lists nested deeper than one level. – Alexej Magura Aug 03 '20 at 14:56
  • 1
    This is a work of art. So few characters, and still nearly impossible to understand. 10/10 best Python code golf I've seen yet ️‍♂️️‍♀️⛳️. Having something this short almost makes up for the fact that Python doesn't have a built-in flatten function. – Jeff Hykin Oct 26 '20 at 02:07
36

Generator version of @unutbu's non-recursive solution, as requested by @Andrew in a comment:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        yield l[i]
        i += 1

Slightly simplified version of this generator:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    while l:
        while l and isinstance(l[0], ltypes):
            l[0:1] = l[0]
        if l: yield l.pop(0)
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 2
    it's a pre-order traversal of the tree formed by the nested lists. only the leaves are returned. Note that this implementation will consume the original data structure, for better or worse. Could be fun to write one that both preserves the original tree, but also doesn't have to copy the list entries. – Andrew Wagner Jan 29 '10 at 08:21
  • 7
    I think you need to test for strings -- eg add "and not isinstance(l[0], basestring)" as in Cristian's solution. Otherwise you get an infinite loop around l[0:1] = l[0] – c-urchin Nov 30 '10 at 15:21
  • This is a good example of making a generator, but as c-urchin mentions, the algorithm itself fails when the sequence contains strings. – Daniel 'Dang' Griffith Oct 26 '12 at 11:58
33

This version of flatten avoids python's recursion limit (and thus works with arbitrarily deep, nested iterables). It is a generator which can handle strings and arbitrary iterables (even infinite ones).

import itertools
import collections

def flatten(iterable, ltypes=collections.Iterable):
    remainder = iter(iterable)
    while True:
        try:
            first = next(remainder)
        except StopIteration:
            break
        if isinstance(first, ltypes) and not isinstance(first, (str, bytes)):
            remainder = itertools.chain(first, remainder)
        else:
            yield first

Here are some examples demonstrating its use:

print(list(itertools.islice(flatten(itertools.repeat(1)),10)))
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

print(list(itertools.islice(flatten(itertools.chain(itertools.repeat(2,3),
                                       {10,20,30},
                                       'foo bar'.split(),
                                       itertools.repeat(1),)),10)))
# [2, 2, 2, 10, 20, 30, 'foo', 'bar', 1, 1]

print(list(flatten([[1,2,[3,4]]])))
# [1, 2, 3, 4]

seq = ([[chr(i),chr(i-32)] for i in range(ord('a'), ord('z')+1)] + list(range(0,9)))
print(list(flatten(seq)))
# ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H',
# 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P',
# 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X',
# 'y', 'Y', 'z', 'Z', 0, 1, 2, 3, 4, 5, 6, 7, 8]

Although flatten can handle infinite generators, it can not handle infinite nesting:

def infinitely_nested():
    while True:
        yield itertools.chain(infinitely_nested(), itertools.repeat(1))

print(list(itertools.islice(flatten(infinitely_nested()), 10)))
# hangs
larsks
  • 277,717
  • 41
  • 399
  • 399
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • 1
    any consensus on whether to use ABC Iterable or ABC Sequence? – wim Sep 15 '13 at 20:09
  • `sets`, `dicts`, `deques`, `listiterators`, `generators`, filehandles, and custom classes with `__iter__` defined are all instances of `collections.Iterable`, but not `collections.Sequence`. The result of flattening a `dict` is a bit iffy, but otherwise, I think `collections.Iterable` is a better default than `collections.Sequence`. It's definitely the more liberal. – unutbu Sep 15 '13 at 20:16
  • @wim: One problem with using `collections.Iterable` is that this includes infinite generators. I've changed my answer handle this case. – unutbu Sep 17 '13 at 09:27
  • 2
    This doesn't seem to work for the 3rd and the 4th examples. It throws `StopIteration`. Also, looks like `while True: first = next(remainder)` could be replaced by `for first in remainder:`. – Georgy May 23 '19 at 12:43
  • 1
    @Georgy this could be fixed with encapsulating the body of flatten in a `try-except StopIteration block`. – baduker Feb 24 '20 at 09:02
17

Pandas has a function that does this. It returns an iterator as you mentioned.

In [1]: import pandas
In [2]: pandas.core.common.flatten([[[1, 2, 3], [4, 5]], 6])
Out[2]: <generator object flatten at 0x7f12ade66200>
In [3]: list(pandas.core.common.flatten([[[1, 2, 3], [4, 5]], 6]))
Out[3]: [1, 2, 3, 4, 5, 6]
Mark Harrison
  • 297,451
  • 125
  • 333
  • 465
  • 1
    Great stuff! For people (like me) who are using pandas anyway, this is a beautifully simple way – rvrvrv Jan 13 '21 at 13:58
  • Unexpected results with a list of pydantic objects, the attributes are also flatten (no more objects in the final list) – Madaray Sep 07 '22 at 13:11
16
def flatten(xs):
    res = []
    def loop(ys):
        for i in ys:
            if isinstance(i, list):
                loop(i)
            else:
                res.append(i)
    loop(xs)
    return res
kev
  • 155,172
  • 47
  • 273
  • 272
11

Here's another answer that is even more interesting...

import re

def Flatten(TheList):
    a = str(TheList)
    b,_Anon = re.subn(r'[\[,\]]', ' ', a)
    c = b.split()
    d = [int(x) for x in c]

    return(d)

Basically, it converts the nested list to a string, uses a regex to strip out the nested syntax, and then converts the result back to a (flattened) list.

clay
  • 1,757
  • 2
  • 20
  • 23
  • If you try to generalize this to something other than int values, it'll be fun with, e.g., `[['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]` :) On the other hand, given a list that contains itself, it'll do a little better than the other answers, raising an exception instead of just looping until you run out of memory/recursing until you exhaust the stack… – abarnert Sep 10 '14 at 04:22
  • The original prompt was about flattening a list of integers. If you just change the list comprehension to d=[x for x in c] it should work fine for your sample. – clay Sep 10 '14 at 13:10
  • First, `[x for x in c]` is just a slow and verbose way to make a copy of `c`, so why would you do that? Second, your code is clearly going to convert `'APPLE ]['` into `'APPLE '`, because it doesn't handle quoting, it just assumes any brackets are list brackets. – abarnert Sep 10 '14 at 18:55
  • Ha! The way your comment got formatted on my computer, I didn't even realize that was supposed to be Apple II as it appeared on the old computers. In any case, my answer to both your questions is that this exercise--for me--is merely an experiment to find a creative solution to flattening a list. I'm not sure I would generalize it to flattening every list out there. – clay Sep 11 '14 at 00:21
  • You just need to `arr_str = str(arr)` and then `[int(s) for s in re.findall(r'\d+', arr_str)]` really. See https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py#L20-L26 – Jorge Orpinel Pérez Jan 22 '19 at 10:03
10

You could use deepflatten from the 3rd party package iteration_utilities:

>>> from iteration_utilities import deepflatten
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(deepflatten(L))
[1, 2, 3, 4, 5, 6]

>>> list(deepflatten(L, types=list))  # only flatten "inner" lists
[1, 2, 3, 4, 5, 6]

It's an iterator so you need to iterate it (for example by wrapping it with list or using it in a loop). Internally it uses an iterative approach instead of an recursive approach and it's written as C extension so it can be faster than pure python approaches:

>>> %timeit list(deepflatten(L))
12.6 µs ± 298 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(deepflatten(L, types=list))
8.7 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit list(flatten(L))   # Cristian - Python 3.x approach from https://stackoverflow.com/a/2158532/5393381
86.4 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(flatten(L))   # Josh Lee - https://stackoverflow.com/a/2158522/5393381
107 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(genflat(L, list))  # Alex Martelli - https://stackoverflow.com/a/2159079/5393381
23.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

I'm the author of the iteration_utilities library.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
9

It was fun trying to create a function that could flatten irregular list in Python, but of course that is what Python is for (to make programming fun). The following generator works fairly well with some caveats:

def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable

It will flatten datatypes that you might want left alone (like bytearray, bytes, and str objects). Also, the code relies on the fact that requesting an iterator from a non-iterable raises a TypeError.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable


>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>>

Edit:

I disagree with the previous implementation. The problem is that you should not be able to flatten something that is not an iterable. It is confusing and gives the wrong impression of the argument.

>>> list(flatten(123))
[123]
>>>

The following generator is almost the same as the first but does not have the problem of trying to flatten a non-iterable object. It fails as one would expect when an inappropriate argument is given to it.

def flatten(iterable):
    for item in iterable:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

Testing the generator works fine with the list that was provided. However, the new code will raise a TypeError when a non-iterable object is given to it. Example are shown below of the new behavior.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(flatten(123))
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    list(flatten(123))
  File "<pyshell#27>", line 2, in flatten
    for item in iterable:
TypeError: 'int' object is not iterable
>>>
Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
7

Here's a simple function that flattens lists of arbitrary depth. No recursion, to avoid stack overflow.

from copy import deepcopy

def flatten_list(nested_list):
    """Flatten an arbitrarily nested list, without recursion (to avoid
    stack overflows). Returns a new list, the original list is unchanged.

    >> list(flatten_list([1, 2, 3, [4], [], [[[[[[[[[5]]]]]]]]]]))
    [1, 2, 3, 4, 5]
    >> list(flatten_list([[1, 2], 3]))
    [1, 2, 3]

    """
    nested_list = deepcopy(nested_list)

    while nested_list:
        sublist = nested_list.pop(0)

        if isinstance(sublist, list):
            nested_list = sublist + nested_list
        else:
            yield sublist
Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192
  • Yes! Very similar to my code at https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py – Jorge Orpinel Pérez Jan 22 '19 at 10:22
  • 1
    This could be made much more efficient by using `collections.deque` and not copying. Assuming you have control over the creation of the nested lists. Also, `deepcopy` hits the recursion limit. – ATOMP Apr 24 '22 at 05:14
6

When trying to answer such a question you really need to give the limitations of the code you propose as a solution. If it was only about performances I wouldn't mind too much, but most of the codes proposed as solution (including the accepted answer) fail to flatten any list that has a depth greater than 1000.

When I say most of the codes I mean all codes that use any form of recursion (or call a standard library function that is recursive). All these codes fail because for every of the recursive call made, the (call) stack grow by one unit, and the (default) python call stack has a size of 1000.

If you're not too familiar with the call stack, then maybe the following will help (otherwise you can just scroll to the Implementation).

Call stack size and recursive programming (dungeon analogy)

Finding the treasure and exit

Imagine you enter a huge dungeon with numbered rooms, looking for a treasure. You don't know the place but you have some indications on how to find the treasure. Each indication is a riddle (difficulty varies, but you can't predict how hard they will be). You decide to think a little bit about a strategy to save time, you make two observations:

  1. It's hard (long) to find the treasure as you'll have to solve (potentially hard) riddles to get there.
  2. Once the treasure found, returning to the entrance may be easy, you just have to use the same path in the other direction (though this needs a bit of memory to recall your path).

When entering the dungeon, you notice a small notebook here. You decide to use it to write down every room you exit after solving a riddle (when entering a new room), this way you'll be able to return back to the entrance. That's a genius idea, you won't even spend a cent implementing your strategy.

You enter the dungeon, solving with great success the first 1001 riddles, but here comes something you hadn't planed, you have no space left in the notebook you borrowed. You decide to abandon your quest as you prefer not having the treasure than being lost forever inside the dungeon (that looks smart indeed).

Executing a recursive program

Basically, it's the exact same thing as finding the treasure. The dungeon is the computer's memory, your goal now is not to find a treasure but to compute some function (find f(x) for a given x). The indications simply are sub-routines that will help you solving f(x). Your strategy is the same as the call stack strategy, the notebook is the stack, the rooms are the functions' return addresses:

x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected 
print(' '.join(y))

The problem you encountered in the dungeon will be the same here, the call stack has a finite size (here 1000) and therefore, if you enter too many functions without returning back then you'll fill the call stack and have an error that look like "Dear adventurer, I'm very sorry but your notebook is full": RecursionError: maximum recursion depth exceeded. Note that you don't need recursion to fill the call stack, but it's very unlikely that a non-recursive program call 1000 functions without ever returning. It's important to also understand that once you returned from a function, the call stack is freed from the address used (hence the name "stack", return address are pushed in before entering a function and pulled out when returning). In the special case of a simple recursion (a function f that call itself once -- over and over --) you will enter f over and over until the computation is finished (until the treasure is found) and return from f until you go back to the place where you called f in the first place. The call stack will never be freed from anything until the end where it will be freed from all return addresses one after the other.

How to avoid this issue?

That's actually pretty simple: "don't use recursion if you don't know how deep it can go". That's not always true as in some cases, Tail Call recursion can be Optimized (TCO). But in python, this is not the case, and even "well written" recursive function will not optimize stack use. There is an interesting post from Guido about this question: Tail Recursion Elimination.

There is a technique that you can use to make any recursive function iterative, this technique we could call bring your own notebook. For example, in our particular case we simply are exploring a list, entering a room is equivalent to entering a sublist, the question you should ask yourself is how can I get back from a list to its parent list? The answer is not that complex, repeat the following until the stack is empty:

  1. push the current list address and index in a stack when entering a new sublist (note that a list address+index is also an address, therefore we just use the exact same technique used by the call stack);
  2. every time an item is found, yield it (or add them in a list);
  3. once a list is fully explored, go back to the parent list using the stack return address (and index).

Also note that this is equivalent to a DFS in a tree where some nodes are sublists A = [1, 2] and some are simple items: 0, 1, 2, 3, 4 (for L = [0, [1,2], 3, 4]). The tree looks like this:

                    L
                    |
           -------------------
           |     |     |     |
           0   --A--   3     4
               |   |
               1   2

The DFS traversal pre-order is: L, 0, A, 1, 2, 3, 4. Remember, in order to implement an iterative DFS you also "need" a stack. The implementation I proposed before result in having the following states (for the stack and the flat_list):

init.:  stack=[(L, 0)]
**0**:  stack=[(L, 0)],         flat_list=[0]
**A**:  stack=[(L, 1), (A, 0)], flat_list=[0]
**1**:  stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**:  stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**:  stack=[(L, 2)],         flat_list=[0, 1, 2, 3]
**3**:  stack=[(L, 3)],         flat_list=[0, 1, 2, 3, 4]
return: stack=[],               flat_list=[0, 1, 2, 3, 4]

In this example, the stack maximum size is 2, because the input list (and therefore the tree) have depth 2.

Implementation

For the implementation, in python you can simplify a little bit by using iterators instead of simple lists. References to the (sub)iterators will be used to store sublists return addresses (instead of having both the list address and the index). This is not a big difference but I feel this is more readable (and also a bit faster):

def flatten(iterable):
    return list(items_from(iterable))

def items_from(iterable):
    cursor_stack = [iter(iterable)]
    while cursor_stack:
        sub_iterable = cursor_stack[-1]
        try:
            item = next(sub_iterable)
        except StopIteration:   # post-order
            cursor_stack.pop()
            continue
        if is_list_like(item):  # pre-order
            cursor_stack.append(iter(item))
        elif item is not None:
            yield item          # in-order

def is_list_like(item):
    return isinstance(item, list)

Also, notice that in is_list_like I have isinstance(item, list), which could be changed to handle more input types, here I just wanted to have the simplest version where (iterable) is just a list. But you could also do that:

def is_list_like(item):
    try:
        iter(item)
        return not isinstance(item, str)  # strings are not lists (hmm...) 
    except TypeError:
        return False

This considers strings as "simple items" and therefore flatten_iter([["test", "a"], "b]) will return ["test", "a", "b"] and not ["t", "e", "s", "t", "a", "b"]. Remark that in that case, iter(item) is called twice on each item, let's pretend it's an exercise for the reader to make this cleaner.

Testing and remarks on other implementations

In the end, remember that you can't print a infinitely nested list L using print(L) because internally it will use recursive calls to __repr__ (RecursionError: maximum recursion depth exceeded while getting the repr of an object). For the same reason, solutions to flatten involving str will fail with the same error message.

If you need to test your solution, you can use this function to generate a simple nested list:

def build_deep_list(depth):
    """Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
    with $depth > 1$ and $l_0 = [0]$.
    """
    sub_list = [0]
    for d in range(1, depth):
        sub_list = [d, sub_list]
    return sub_list

Which gives: build_deep_list(5) >>> [4, [3, [2, [1, [0]]]]].

cglacet
  • 8,873
  • 4
  • 45
  • 60
6

Although an elegant and very pythonic answer has been selected I would present my solution just for the review:

def flat(l):
    ret = []
    for i in l:
        if isinstance(i, list) or isinstance(i, tuple):
            ret.extend(flat(i))
        else:
            ret.append(i)
    return ret

Please tell how good or bad this code is?

Xolve
  • 22,298
  • 21
  • 77
  • 125
  • 2
    Use `isinstance(i, (tuple, list))`. Initializing empty variables is a flag for me to look to alternate code structures, typically comprehensions, generators, recursion, etc. – dansalmo Jul 06 '13 at 19:19
  • 3
    `return type(l)(ret)` will get you the same container type back as was passed in, also. :) – dash-tom-bang Apr 16 '14 at 20:17
  • @dash-tom-bang Can you please explain what it means in a bit detail. – Xolve Apr 19 '14 at 07:34
  • 2
    If you pass in a list, you probably want a list back. If you pass in a tuple, you probably want a tuple back. If you pass in a mishmash of the two, you'll get whatever the outer enclosing thing was. – dash-tom-bang Jun 14 '14 at 01:04
5

I didn't go through all the already available answers here, but here is a one liner I came up with, borrowing from lisp's way of first and rest list processing

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

here is one simple and one not-so-simple case -

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]

>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 
Shreyas
  • 1,404
  • 16
  • 10
  • 3
    It's not a one liner. No matter how much you attempt to fit it into one, the `def foo():` is a separate line. Also, this is very unreadable. – cs95 Jan 09 '18 at 22:44
  • 1
    I've de-one-line-ified the code, and did some further refactoring. (edit is pending peer review as I write this) This particular method seemed very readable to me, though the original code did need some refactoring. – Emilio M Bumachar Feb 05 '18 at 17:20
  • Please do not edit the answer. If you feel the need to "refactor", feel free to post as your own answer. There is a reason why the code is presented the way it is. It is to emphasise that the approach came from lisp. You can plain ignore the "one-liner" part of it - it was not intended as some kind of boasting. It was, again, to indicate that the thought behind it is still "one-liner": that of first and rest list processing. – Shreyas Jun 16 '21 at 03:17
5

I prefer simple answers. No generators. No recursion or recursion limits. Just iteration:

def flatten(TheList):
    listIsNested = True

    while listIsNested:                 #outer loop
        keepChecking = False
        Temp = []

        for element in TheList:         #inner loop
            if isinstance(element,list):
                Temp.extend(element)
                keepChecking = True
            else:
                Temp.append(element)

        listIsNested = keepChecking     #determine if outer loop exits
        TheList = Temp[:]

    return TheList

This works with two lists: an inner for loop and an outer while loop.

The inner for loop iterates through the list. If it finds a list element, it (1) uses list.extend() to flatten that part one level of nesting and (2) switches keepChecking to True. keepchecking is used to control the outer while loop. If the outer loop gets set to true, it triggers the inner loop for another pass.

Those passes keep happening until no more nested lists are found. When a pass finally occurs where none are found, keepChecking never gets tripped to true, which means listIsNested stays false and the outer while loop exits.

The flattened list is then returned.

Test-run

flatten([1,2,3,4,[100,200,300,[1000,2000,3000]]])

[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]

clay
  • 1,757
  • 2
  • 20
  • 23
  • I like simple too. In this case though, you iterate over the list as many times as there are nestings or levels. Could get expensive. – telliott99 Jan 13 '11 at 11:56
  • @telliott99: You're right if your lists are really big and/or nested to great depths. However, if that isn't the case, then the simpler solution works just as well, and without the deep magic of some of the other answers. There is a place for multi-stage recursive generator comprehensions, but I'm not convinced that should be where you look first. (I guess you know where I fall in the "Worse is Better" debate.) – clay Jan 14 '11 at 15:11
  • @telliott99: Or to put that another way, you won't have to "try to Grok" my solution. If performance isn't a bottleneck, what matters most to you as a programmer? – clay Jan 14 '11 at 15:18
  • Simpler solutions have less logic. Recursion is a pretty fundamental programming construct that anyone who considers themselves a programmer should be completely comfortable with. Generators are very much the Python Way and (along with comprehensions) are something that any professional Python programmer should grok instantly. – dash-tom-bang Apr 16 '14 at 20:15
  • 1
    I agree about recursion. When I wrote my answer, python still broke recursion at 1000 cycles. Have they changed this? As for being a professional python programmer, I'm not. Moreover, I imagine many people programming in python do not do so full time. – clay Apr 16 '14 at 23:39
3

I'm not sure if this is necessarily quicker or more effective, but this is what I do:

def flatten(lst):
    return eval('[' + str(lst).replace('[', '').replace(']', '') + ']')

L = [[[1, 2, 3], [4, 5]], 6]
print(flatten(L))

The flatten function here turns the list into a string, takes out all of the square brackets, attaches square brackets back onto the ends, and turns it back into a list.

Although, if you knew you would have square brackets in your list in strings, like [[1, 2], "[3, 4] and [5]"], you would have to do something else.

diligar
  • 413
  • 5
  • 11
3

Just use a funcy library: pip install funcy

import funcy


funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list
Georgy
  • 12,464
  • 7
  • 65
  • 73
ADR
  • 1,255
  • 9
  • 20
  • 1
    FYI: it uses recursive solution: [link to source](https://github.com/Suor/funcy/blob/f29f132bc0cc9458556188b62d0de51749d27f5b/funcy/seqs.py#L183-L192) – Georgy May 23 '19 at 13:32
2

Here's the compiler.ast.flatten implementation in 2.7.5:

def flatten(seq):
    l = []
    for elt in seq:
        t = type(elt)
        if t is tuple or t is list:
            for elt2 in flatten(elt):
                l.append(elt2)
        else:
            l.append(elt)
    return l

There are better, faster methods (If you've reached here, you have seen them already)

Also note:

Deprecated since version 2.6: The compiler package has been removed in Python 3.

pradyunsg
  • 18,287
  • 11
  • 43
  • 96
2

I'm surprised no one has thought of this. Damn recursion I don't get the recursive answers that the advanced people here made. anyway here is my attempt on this. caveat is it's very specific to the OP's use case

import re

L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[\[\]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)

output:

[1, 2, 3, 4, 5, 6]
Zion
  • 1,570
  • 6
  • 24
  • 36
  • this only works for types (like int) which are convertible to string and back. something with the complexity of regular expressions is also not needed to tackle such a simple problem. If you want a simple solution, pradyunsg's is best. – Jake Schmidt Oct 13 '20 at 04:43
2

I am aware that there are already many awesome answers but i wanted to add an answer that uses the functional programming method of solving the question. In this answer i make use of double recursion :

def flatten_list(seq):
    if not seq:
        return []
    elif isinstance(seq[0],list):
        return (flatten_list(seq[0])+flatten_list(seq[1:]))
    else:
        return [seq[0]]+flatten_list(seq[1:])

print(flatten_list([1,2,[3,[4],5],[6,7]]))

output:

[1, 2, 3, 4, 5, 6, 7]
Leo wahyd
  • 207
  • 1
  • 14
2

No recursion or nested loops. A few lines. Well formatted and easy to read:

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)

From my own code at https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py

aeportugal
  • 201
  • 5
  • 9
Jorge Orpinel Pérez
  • 6,361
  • 1
  • 21
  • 38
  • 1
    Thank you, this is the easiest for me to understand and works well for me; well done. Edit; also just say your flatten_trick, love it! – Nathaniel Hoyt Jan 03 '23 at 20:12
1

totally hacky but I think it would work (depending on your data_type)

flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
1

Here is another py2 approach, Im not sure if its the fastest or the most elegant nor safest ...

from collections import Iterable
from itertools import imap, repeat, chain


def flat(seqs, ignore=(int, long, float, basestring)):
    return repeat(seqs, 1) if any(imap(isinstance, repeat(seqs), ignore)) or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

It can ignore any specific (or derived) type you would like, it returns an iterator, so you can convert it to any specific container such as list, tuple, dict or simply consume it in order to reduce memory footprint, for better or worse it can handle initial non-iterable objects such as int ...

Note most of the heavy lifting is done in C, since as far as I know thats how itertools are implemented, so while it is recursive, AFAIK it isn't bounded by python recursion depth since the function calls are happening in C, though this doesn't mean you are bounded by memory, specially in OS X where its stack size has a hard limit as of today (OS X Mavericks) ...

there is a slightly faster approach, but less portable method, only use it if you can assume that the base elements of the input can be explicitly determined otherwise, you'll get an infinite recursion, and OS X with its limited stack size, will throw a segmentation fault fairly quickly ...

def flat(seqs, ignore={int, long, float, str, unicode}):
    return repeat(seqs, 1) if type(seqs) in ignore or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

here we are using sets to check for the type so it takes O(1) vs O(number of types) to check whether or not an element should be ignored, though of course any value with derived type of the stated ignored types will fail, this is why its using str, unicode so use it with caution ...

tests:

import random

def test_flat(test_size=2000):
    def increase_depth(value, depth=1):
        for func in xrange(depth):
            value = repeat(value, 1)
        return value

    def random_sub_chaining(nested_values):
        for values in nested_values:
            yield chain((values,), chain.from_iterable(imap(next, repeat(nested_values, random.randint(1, 10)))))

    expected_values = zip(xrange(test_size), imap(str, xrange(test_size)))
    nested_values = random_sub_chaining((increase_depth(value, depth) for depth, value in enumerate(expected_values)))
    assert not any(imap(cmp, chain.from_iterable(expected_values), flat(chain(((),), nested_values, ((),)))))

>>> test_flat()
>>> list(flat([[[1, 2, 3], [4, 5]], 6]))
[1, 2, 3, 4, 5, 6]
>>>  

$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5
Samy Vilar
  • 10,800
  • 2
  • 39
  • 34
1

I used recursive to solve nested list with any depth

def combine_nlist(nlist,init=0,combiner=lambda x,y: x+y):
    '''
    apply function: combiner to a nested list element by element(treated as flatten list)
    '''
    current_value=init
    for each_item in nlist:
        if isinstance(each_item,list):
            current_value =combine_nlist(each_item,current_value,combiner)
        else:
            current_value = combiner(current_value,each_item)
    return current_value

So after i define function combine_nlist, it is easy to use this function do flatting. Or you can combine it into one function. I like my solution because it can be applied to any nested list.

def flatten_nlist(nlist):
    return combine_nlist(nlist,[],lambda x,y:x+[y])

result

In [379]: flatten_nlist([1,2,3,[4,5],[6],[[[7],8],9],10])
Out[379]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Alex Lisovoy
  • 5,767
  • 3
  • 27
  • 28
Oldyoung
  • 567
  • 4
  • 8
  • "nested list with any depth" not true. Just try you'll see: `current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded` – cglacet Aug 01 '18 at 16:48
  • hmmm I are you trying to flaten list with more than 1000 layers? – Oldyoung Aug 01 '18 at 20:19
  • Of course, that's the whole point of the discussion about recursive vs. iterative solutions. If you know in advance that the number of layers is < than 1000 then the most simple solution will work. When you say "any depth" this includes list with depth > 1000. – cglacet Aug 02 '18 at 06:44
1

Without using any library:

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]
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
1

Using itertools.chain:

import itertools
from collections import Iterable

def list_flatten(lst):
    flat_lst = []
    for item in itertools.chain(lst):
        if isinstance(item, Iterable):
            item = list_flatten(item)
            flat_lst.extend(item)
        else:
            flat_lst.append(item)
    return flat_lst

Or without chaining:

def flatten(q, final):
    if not q:
        return
    if isinstance(q, list):
        if not isinstance(q[0], list):
            final.append(q[0])
        else:
            flatten(q[0], final)
        flatten(q[1:], final)
    else:
        final.append(q)
Saksham Varma
  • 2,122
  • 13
  • 15
1

The easiest way is to use the morph library using pip install morph.

The code is:

import morph

list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 4, 5, 6]
YPCrumble
  • 26,610
  • 23
  • 107
  • 172
1

I am a dumb guy so I'll give a "dumb" solution. All that recursion hurts my brain.

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, container)
        else:
            container.append(item)

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

I get that it's using a side effect but well that's to the best of my comprehension of recursion can go

vlz
  • 911
  • 1
  • 10
  • 18
halcyonjuly7
  • 295
  • 1
  • 3
  • 11
1

This will flatten a list or dictionary (or list of lists or dictionaries of dictionaries etc). It assumes that the values are strings and it creates a string that concatenates each item with a separator argument. If you wanted you could use the separator to split the result into a list object afterward. It uses recursion if the next value is a list or a string. Use the key argument to tell whether you want the keys or the values (set key to false) from the dictionary object.

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=', ')

yields:

just, a, test, to, try, right, now, or, later, today, dictionary_test, dictionary_test_two, my power is 9000
Georgy
  • 12,464
  • 7
  • 65
  • 73
Matt Farguson
  • 325
  • 2
  • 6
1

This is a simple implement of flatten on python2

flatten=lambda l: reduce(lambda x,y:x+y,map(flatten,l),[]) if isinstance(l,list) else [l]

test=[[1,2,3,[3,4,5],[6,7,[8,9,[10,[11,[12,13,14]]]]]],]
print flatten(test)

#output [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Statham
  • 4,000
  • 2
  • 32
  • 45
1

I don't see anything like this posted around here and just got here from a closed question on the same subject, but why not just do something like this(if you know the type of the list you want to split):

>>> 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()]

You would need to know the type of the elements but I think this can be generalised and in terms of speed I think it would be faster.

Abhijit
  • 62,056
  • 18
  • 131
  • 204
Bogdan
  • 8,017
  • 6
  • 48
  • 64
  • This is clever (and probably fast)... but it's not very pythonic. – DanB Jun 09 '12 at 04:54
  • 8
    "why not just do something like this" you say? Because it is very easy to break! Very bad idea. One example, what if your items are strings, not ints? Then if a string contains a '[' you are doomed. And what if your items have no good (or very long) string representation? – gb. Aug 23 '12 at 04:40
  • @gb. Well what if this was what the op needed? and the example was clearly a list of `ints` so "what if's" don't apply here, had the OP stated otherwise, but then again he didn't so, this is the one of the simplest and most valid answers according to what was given. – Zion Nov 14 '15 at 06:36
  • 2
    Well sorry, "what ifs" apply, careful considerations of all "what ifs" is the blood and guts of programming. – gb. Nov 16 '15 at 10:34
1

This is how I did it with recursion:

def flatten(x):
    if not any(isinstance(e, list) for e in x):
        return x
    while type(x[-1]) == int:
        x = [x[-1]] + [x[:-1]]
    return flatten(x = x + x.pop(-1))

Or even:

def flatten(x):
    if not any(isinstance(e, list) for e in x):
        return x
    return flatten(x = x + x.pop([isinstance(e, list) for e in x].index(1)))
arara
  • 167
  • 1
  • 8
1

No frills. Only chills.

recursive_list_of_lists = [1,2,3,[1,2,[[3,4,[5]],7,0,1,10],100,[101,[101,[[101]],2]],0]]

k = []

def flatten(subl):
    for i in subl:
        if type(i) != type([1]):
            k.append(i)
        else:
            flatten(i)

flatten(recursive_list_of_lists)

print(k)

[1, 2, 3, 1, 2, 3, 4, 5, 7, 0, 1, 10, 100, 101, 101, 101, 2, 0]

Rex
  • 117
  • 8
0

If you like recursion, this might be a solution of interest to you:

def f(E):
    if E==[]: 
        return []
    elif type(E) != list: 
        return [E]
    else:
        a = f(E[0])
        b = f(E[1:])
        a.extend(b)
        return a

I actually adapted this from some practice Scheme code that I had written a while back.

Enjoy!

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
0

Shamelessly taken from my own answer to another question.

This function

  • Does not use isinstance, because it's evil and breaks duck typing.
  • Uses reduce recursively. There has to be an answer using reduce.
  • Works with arbitrary nested-lists whose elements are either nested-lists, or non-nested lists of atoms, or atoms (subjected to recursion limit).
  • Does not LBYL.
  • But not with nested-lists that contain strings as atoms.

Code below:

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]
Community
  • 1
  • 1
Cong Ma
  • 10,692
  • 3
  • 31
  • 47
0

We can also use the 'type' function of python. When iterating the list we check if the item is a list or not. If not we 'append' it else we 'extend' it. Here is a sample code -

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

Output:

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

For more info on append() and extend() check this website : https://docs.python.org/2/tutorial/datastructures.html

noobcoder
  • 151
  • 2
  • 2
  • 13
0

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)
Vlady Veselinov
  • 4,678
  • 5
  • 23
  • 49
Fuji Komalan
  • 1,979
  • 16
  • 25
0

I'm new to python and come from a lisp background. This is what I came up with (check out the var names for 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]

Seems to work. Test:

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

returns:

[1, 2, 3, 4, 5, 6, 7, 8, 1, 2]
Michael Puckett
  • 465
  • 1
  • 5
  • 7
0

Iterative solution with Python 3

This solution may work with all objects except str and bytes.

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, 'next element is 5', 5, 6, 3, 4, 5, 'foo1', 'foo2', 'foo3']

[10]

DeaD_EyE
  • 433
  • 5
  • 10
0

From my previous answer, this function flattens most cases I can think of. I believe this works down to python 2.3.

def flatten(item, keepcls=(), keepobj=()):
    if not hasattr(item, '__iter__') or isinstance(item, keepcls) or item in keepobj:
        yield item
    else:
        for i in item:
            for j in flatten(i, keepcls, keepobj + (item,)):
                yield j

Circular lists

>>> list(flatten([1, 2, [...], 3]))
[1, 2, [1, 2, [...], 3], 3]

Depth first lists

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

Nested repeated lists:

>>> list(flatten([[1,2],[1,[1,2]],[1,2]]))
[1, 2, 1, 1, 2, 1, 2]

Lists with dicts (or other objects to not flatten)

>>> list(flatten([1,2, {'a':1, 'b':2}, 'text'], keepcls=(dict, str)))
[1, 2, {'a': 1, 'b': 2}, 'text']

Any iterables

>>> list(flatten((x for x in [1,2, set([3,(4,5),6])])))
[1, 2, 4, 5, 3, 6]

You may want to keep some default classes in keepcls to make calling the function more terse.

wihlke
  • 2,455
  • 1
  • 19
  • 18
0
def nested_list(depth):
    l = [depth]
    for i in range(depth-1, 0, -1):
        l = [i, l]
    return l

nested_list(10)

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

def Flatten(ul):
    fl = []
    for i in ul:
        if type(i) is list:
            fl += Flatten(i)
        else:
            fl += [i]
    return fl

Flatten(nested_list(10))

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

Benchmarking

l = nested_list(100)

https://stackoverflow.com/a/2158532

import collections

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el
%%timeit -n 1000
list(flatten(l))

320 µs ± 14.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%%timeit -n 1000
Flatten(l)

60 µs ± 10.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

list(flatten(l)) == Flatten(l)

True

Alexey Golyshev
  • 792
  • 6
  • 11
0
def flatten(item) -> list:
    if not isinstance(item, list): return item
    return reduce(lambda x, y: x + [y] if not isinstance(y, list) else x + [*flatten(y)], item, [])

Two-line reduce function.

Union find
  • 7,759
  • 13
  • 60
  • 111
0

I modified the code of the accepted answer and added a keyword max_depth to only flatten up to a specified depth. max_depth=0 means, the list stays as it is. Maybe somebody can use it:

def flatten(l, __depth=0, max_depth=100):

    for el in l:

        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):

            __depth += 1
            if __depth <= max_depth:
                yield from flatten(el, __depth=__depth, max_depth=max_depth)
            else:
                yield el
            __depth -= 1

        else:

            yield el

Some examples:

# A
l = []
depth = 5
for i in range(depth):
    el = i
    for j in range(i):
        el = [el]
    l.append(el)
# [0, [1], [[2]], [[[3]]], [[[[4]]]]]

for i in range(depth):
    print(list(flatten_gen(l, max_depth=i)))
# [0, [1], [[2]], [[[3]]], [[[[4]]]]]
# [0,  1,   [2],   [[3]],   [[[4]]]]
# [0,  1,    2,     [3],     [[4]]]
# [0,  1,    2,      3,       [4]]
# [0,  1,    2,      3,        4]


# B
l = [[1, 2], [3, 4, [5, 6, [7, [8, [9]]], 10], 12, [13]], 14, [15]]

for i in range(6):
    print(list(flatten_gen(l, max_depth=i)))
# [[1, 2], [3, 4, [5, 6, [7, [8, [9]]], 10], 12, [13]], 14, [15]]
# [ 1, 2,   3, 4, [5, 6, [7, [8, [9]]], 10], 12, [13],  14,  15]
# [ 1, 2,   3, 4,  5, 6, [7, [8, [9]]], 10,  12,  13,   14,  15]
# [ 1, 2,   3, 4,  5, 6,  7, [8, [9]],  10,  12,  13,   14,  15]
# [ 1, 2,   3, 4,  5, 6,  7,  8, [9],   10,  12,  13,   14,  15]
# [ 1, 2,   3, 4,  5, 6,  7,  8,  9,    10,  12,  13,   14,  15]
scleronomic
  • 4,392
  • 1
  • 13
  • 43
0

A much more efficient version of this answer: https://stackoverflow.com/a/20495215/8887313

If you have control over the creation of the list and are willing to mutate it, then it is much more efficient to use a deque (instead of pop(0) and list contatenation).

import collections

def flatten_and_consume(nested_deque: collections.deque):
    while nested_deque:
        elt = nested_deque.popleft()

        elt_is_sublist = isinstance(elt, collections.deque)
        if elt_is_sublist:
            nested_deque.extendleft(reversed(elt))
        else:
            yield elt
ATOMP
  • 1,311
  • 10
  • 29
-1

Most of the answers are using a loop to go through the items. Here I have a variant that is using an EAFP way to do things: try to get an iterator on your input, if it succeeds run your function first on the first element, next on the remainder of this iterator. If you can't get an iterator, or if it's a string or a bytes object: yield the element.

Thanks to the suggestion from A. Kareem, who found out that my code was very slow, due to the fact that the recursion took too long for string and byte objects, here is an improved version of my code.

def flatten(x, it = None):
    try:
        if type(x) in (str, bytes):
            yield x
        else:
            if not it:
                it = iter(x)
            yield from flatten(next(it))
        if type(x) not in (str, bytes):
            yield from flatten(x, it)
    except StopIteration:
        pass
    except Exception:
        yield x

oldlist = [1,[[[["test",3]]]],((4,5,6)),[ bytes("test", encoding="utf-8"),7,[8,9]]]
newlist = [ x for x in flatten(oldlist) ]
print(newlist)
# [1, 'test', 3, 4, 5, 6, b'test', 7, 8, 9]
Marko
  • 372
  • 2
  • 7
  • I applaud your idea of using generators but your code seems to take awfully long to run (it takes more than an hour to run over a single list with 1 million empty strings) since every time it reaches a string/byte object it only yields when it reaches a maximum recursion depth limit, and it repeats the whole process for every string/byte object. (which im pretty sure is an unintended side effect from catching all exceptions) Although I really like your idea so I implemented a correct version, I would like to edit your answer to fix the problem but I think that would be awfully rude of me. – A Kareem Oct 19 '20 at 14:36
  • Thank you for your comment, @AKareem, I hope I've improved my code now, I will look into your version too :-) – Marko Oct 20 '20 at 16:05
-1

This solution is based on the python's iteration-utilities library and its function deepflatten

from iteration_utilities import deepflatten
list(deepflatten(test))
MefAldemisov
  • 867
  • 10
  • 21
-1

I've tried solving it without using any library. Simply using two nested functions does the job.

def first(list_to_flatten):
    a = []

    def second(list_to_flatten):
        for i in list_to_flatten:
            if type(i) is not list:
                a.append(i)
            else:
                list_to_flatten = i
                second(list_to_flatten)

    second(list_to_flatten)
    return a

list_to_flatten = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]
a = first(list_to_flatten)
print(a)

>>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
archit jain
  • 179
  • 2
  • 6
-2
L2 = [o for k in [[j] if not isinstance(j,list) else j for j in [k for i in [[m] if not 
isinstance(m,list) else m for m in L] for k in i]] for o in k]
RAS
  • 8,100
  • 16
  • 64
  • 86
fotocoder
  • 45
  • 3
-2

Simple Function Without using instances

L = [[[1, 2, 3], [4, 5]], 6]
l1 = []
def FlattenList(List1):
    for i in range(len(List1)):
        if type(List1[i]) == type([]):
            FlattenList(List1[i])
        else:
            l1.append(List1[i])
    return l1


FlattenList(L)
[1, 2, 3, 4, 5, 6]
Shivpe_R
  • 1,022
  • 2
  • 20
  • 31
-2

The think that following would probably work in python 3:

def get_flat_iter(xparent):
    try:
        r = xparent
        if hasattr(xx, '__iter__'):
            iparent = iter(xparent)
            if iparent != xparent:
                r = map(a, xparent)
    finally:
         pass
    return r

irregular_list = [1, [2, [3, 4]]]
flat_list = list(irregular_list)
print(flat_list) # [1, 2, 3, 4]
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42