-1

I have a small problem where I lack quite some Python experience. Assuming a list:

list=[[40, 20, 45, 40], [[20, 10, 10, 30, 45, 20], [30, 20, 20, 30]]]

I want to do something I call 'partial flattening' because the expected output is:

[[40, 20, 45, 40], [20, 10, 10, 30, 45, 20], [30, 20, 20, 30]]

Notice before the lentgh of list was 2 while the expected output has length of 3. The problem is that I don't know in advance which element from list is nested one layer deeper.

This answer and many others didn't help much, because the flattening goes to far. Following the question the output is:

list=[40, 20, 45, 40, [20, 10, 10, 30, 45, 20], [30, 20, 20, 30]]

(notice the missing brackets for the first 4 elements from the list.

skrat
  • 648
  • 2
  • 10
  • 27
  • 2
    So, you want a list of all the inner-most lists? Can there be an input list like `[1,2,[3,4]]`, and if so, how should that be flattened? – tobias_k Aug 04 '17 at 09:15
  • @tobias_k An input list like that is not possible. – skrat Aug 04 '17 at 09:16
  • 1
    You are telling us what the output for that specific example is, and I understand this. But what is this decision based on? What is the rule on what should be flattened? It’s totally unclear what the logic is supposed to be like? And once you are there, what problem do you have solving it yourself considering you have already seen and used other flattening methods? – poke Aug 04 '17 at 09:17
  • 4
    Please, dont call `list` to *lists* – Avión Aug 04 '17 at 09:18
  • *What is the rule that tells you* how far to flatten? Do you always want everything flattened to the same depth, or just what? – Karl Knechtel Sep 06 '22 at 04:45

2 Answers2

1

You can create a recursive function, testing whether all the inner elements are lists. If so, recursively apply the algorithm, otherwise yield the list itself.

def inner_lists(lst):
    if all(isinstance(x, list) for x in lst):
        for inner in lst:
            for x in inner_lists(inner):
                yield x
    else:
        yield lst

>>> lst = [[40, 20, 45, 40], [[20, 10, 10, 30, 45, 20], [30, 20, 20, 30]]]
>>> list(inner_lists(lst))
[[40, 20, 45, 40], [20, 10, 10, 30, 45, 20], [30, 20, 20, 30]]

Or using yield from (Python 3.3 and later):

def inner_lists(lst):
    if all(isinstance(x, list) for x in lst):
        yield from (x for inner in lst for x in inner_lists(inner))
    else:
        yield lst

Or without generators:

def inner_lists(lst):
    if all(isinstance(x, list) for x in lst):
        return [x for inner in lst for x in inner_lists(inner)]
    else:
        return [lst]
tobias_k
  • 81,265
  • 12
  • 120
  • 179
1

If your lists can only contains numbers or sub lists (not both of them) you can use following generator function:

In [25]: def partial_flattening(lst):
             for sub in lst:
                 if isinstance(sub[0], list):
                     yield from sub # in python 2.x use for i in sub: yeild i
                 else:          
                     yield sub
   ....:                 

In [26]: list(partial_flattening(lst))
Out[36]: [[40, 20, 45, 40], [20, 10, 10, 30, 45, 20], [30, 20, 20, 30]]

If there may be combination of both types then based on what you want to do with the numbers you can customize the function.

Or as a more concise approach you can use yield within list comprehension:

In [29]: list([(yield from i) if isinstance(i[0], list) else (yield i) for i in lst])
Out[29]: [[40, 20, 45, 40], [20, 10, 10, 30, 45, 20], [30, 20, 20, 30]]
Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • Did not know you could use `yield` in a list comp. However, both works only for a single level of nesting (which might be entirely okay given OP's example) – tobias_k Aug 04 '17 at 11:40