1

I have a list flattening algorithm:

def flatten(s):
    if not s:
        return s
    if isinstance(s[0], (list, set, tuple)):
        return flatten(s[0]) + flatten(s[1:])
    return s[:1] + flatten(s[1:])

But it doesn't work if you combine two different data types. A list inside a tuple for example. Is there a way to do this? Also would flattening dicts be possible? The values, not the keys.

  • what exactly do you mean by 'flattening'? Sample input and output would be useful – Jonathan S Oct 01 '19 at 11:26
  • This answer uses itertools.chain() : https://stackoverflow.com/a/953097/509840. Here's the documentation for Python 3.7: https://docs.python.org/3.7/library/itertools.html#itertools.chain – rajah9 Oct 01 '19 at 11:31

4 Answers4

3

Using itertools -> chain or standard libraries is usually recommended, but for arbitrarily nested containers:

xlen = len(x)
result = []    

def flatten(x, i, xlen, result):
  if i >= xlen:
    return
  if not isinstance(x[i], (list, set, tuple)):
    result.append(x[i])
  else:
    flatten(list(x[i]), 0, len(x[i]), result)
  flatten(x, i+1, xlen, result)

you can add corner cases for empty containers, etc.

ksha
  • 2,007
  • 1
  • 19
  • 22
2

There are several algorithms on SO to flatten nested lists and tuples, but we will stick with yours. The first observation is that your algorithm as written will not work with set instances since they are not subscriptable. So sets have to go:

def flatten(s):
    if not s:
        return s
    if isinstance(s[0], (list, tuple)):
        return list(flatten(s[0])) + list(flatten(s[1:]))
    return list(s[:1]) + list(flatten(s[1:]))

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

Prints:

[1, 2, 3, 4, 5, 6]
Booboo
  • 38,656
  • 3
  • 37
  • 60
1

If you are looking for optimization and some fun ... Recursion is okey, but don't forget about RecursionError.

Input:

from datetime import datetime
from typing import Sequence


def timer(f):
    def wrapped(s):
        start = datetime.now()
        result = f(s)
        print(datetime.now() - start)
        return result
    return wrapped


@timer
def str_method(s: Sequence):
    return [int(x) for x in
            str(s).replace("(", "[").replace(")", "]")[1:-1].replace("[", "").replace("]", "")
            if x not in [",", " "]]


def flatten(s: Sequence):
    if not s:
        return s
    if isinstance(s[0], (list, tuple)):
        return list(flatten(s[0])) + list(flatten(s[1:]))
    return list(s[:1]) + list(flatten(s[1:]))

if __name__ == '__main__':
    s = [1, 2, (3, 4, (5, 6))]
    start = datetime.now()
    print(flatten(s))
    print(datetime.now() - start)
    print(str_method(s))
    print("-")
    s = [(x, ) for x in range(100)]
    start = datetime.now()
    flatten(s)
    print(datetime.now() - start)
    str_method(s)
    print("-")
    s = [(x, ) for x in range(100000)]
    start = datetime.now()
    try:
        print(flatten(s))
    except RecursionError:
        print("RecursionError")
    str_method(s)

Output:

[1, 2, 3, 4, 5, 6]
0:00:00.000022 # flatten
0:00:00.000041 # str_method
[1, 2, 3, 4, 5, 6]
-
0:00:00.000369 # flatten
0:00:00.000122 # str_method
-
RecursionError # flatten
0:00:00.186894 # str_method
Artem Getmanskiy
  • 193
  • 2
  • 16
0

The other answers are all valid, but if you are importing sympy into your code, maybe use it's flatten method? It's very fast, and suits your needs.

Legorooj
  • 2,646
  • 2
  • 15
  • 35
  • I've selected this answer only because i actually happen to by using sympy! What are the chances? –  Oct 08 '19 at 10:20