vferraz
vferraz

Reputation: 469

Flattening a multilevel list of lists to a single level

I need to flatten all the levels of a multi-level list:

import itertools

pool = [[[[[0,2],[1,3]],[[3,2],[1,0]]],"PdPd","PrisonerDilemma"], 
[[[[0,3],[1,2]],[[2,3],[1,0]]],"ShSh","StagHunt"],
[[[[1,2],[3,0]],[[3,2],[1,0]]],"ChCh","Chicken"],
[[[[2,1],[0,3]],[[3,1],[0,2]]],"BaBa","Battle"]]

def flat3 (pool):
    for game in pool:
        l = list(itertools.chain.from_iterable(game[0]))
        print(l)

The result:

flat3 (pool)
[[0, 2], [1, 3], [3, 2], [1, 0]]
[[0, 3], [1, 2], [2, 3], [1, 0]]
[[1, 2], [3, 0], [3, 2], [1, 0]]
[[2, 1], [0, 3], [3, 1], [0, 2]]

So, the objective is to isolate and return the first level in each item, containing only the numbers, such as:

[[[[[0,2],[1,3]],[[3,2],[1,0]]],"PdPd","PrisonerDilemma"]

Then I need everything to be flattened to the same level, like:

[0,2,1,3,3,2,1,0]

I know there is a lot of material on this topic and I have found and tried many ways of doing this, however none seems to work with more than one level, I want to know if there is an efficient way to do this, without repeating the same command many times. Could anyone help me?

Upvotes: 4

Views: 4227

Answers (4)

Donnie Cameron
Donnie Cameron

Reputation: 786

This works for lists of mixed types, but considers anything other than a list to be atomic.

def flatten(l):
    if not isinstance(l, list):
        return [l]
    flat = []
    for sublist in l:
        flat.extend(flatten(sublist))
    return flat

If you call the function with something like this:

[
    1, 
    2, 
    [3, 4, [5, 6], [7, 8, [9, 0]]],
    "hello world", 
    ["foo", "bar", ["a", "b", ["c"], None]]
]

It'll do the right thing:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 'hello world', 'foo', 'bar', 'a', 'b', 'c', None]

Upvotes: 1

splash58
splash58

Reputation: 26153

def flat(pool):
    res = []
    for v in pool:
        if isinstance(v, list):
          res += flat(v)
        else:
          if isinstance(v, int):
            res.append(v)
    return res  

Upvotes: 3

Aaditya Ura
Aaditya Ura

Reputation: 12669

If your last output is all digits of every nested list then you can just fetch them using regular expression :

One line solution :

import re

pool = [[[[[0,2],[1,3]],[[3,2],[1,0]]],"PdPd","PrisonerDilemma"],
[[[[0,3],[1,2]],[[2,3],[1,0]]],"ShSh","StagHunt"],
[[[[1,2],[3,0]],[[3,2],[1,0]]],"ChCh","Chicken"],
[[[[2,1],[0,3]],[[3,1],[0,2]]],"BaBa","Battle"]]

pattern=r'\d+'

print([[int(i) for i in re.findall(pattern,str(i))] for i in pool ])

output:

[[0, 2, 1, 3, 3, 2, 1, 0], [0, 3, 1, 2, 2, 3, 1, 0], [1, 2, 3, 0, 3, 2, 1, 0], [2, 1, 0, 3, 3, 1, 0, 2]]

Detailed solution:

Above list comprehension is same as:

for i in pool:
    nested_list=[]
    for i in re.findall(pattern, str(i)):
        nested_list.append(int(i))
    print(nested_list)

Upvotes: 1

Adam Smith
Adam Smith

Reputation: 54213

The first step, as usual, is to look at your data structure.

el = [
         [
             [[0,2],[1,3]],
             [[3,2],[1,0]]
         ],
         "PdPd",
         "PrisonerDilemma"
     ]

This is each individual element in your structure. We'll ignore for the time being that this outermost structure almost certainly shouldn't be a list (it looks more like a tuple to me) and just focus on what we've got.

el[0] = [ [[0, 2], [1, 3]],
          [[3, 2], [1, 0]] ]
el[1] = # a string
el[2] = # another string, we don't care about these

Now we've reduced it to a list of lists of LISTS of numbers. This one we can operate on.

def flatten(lst):
    for el in lst:
        if isinstance(el, list):  # N.B. this only works for lists, not general
                                  # collections e.g. sets, tuples, dicts, etc...
            # recurse
            yield from flatten(el)
        else:
            # generate
            yield el

your result then being to apply this function to the first element of each item in the outermost list.

result = [flatten(sublst[0]) for sublst in big_list]

Note that this creates a list of generator objects, which need to be consumed to actually produce values. If you actually need the lists themselves for some reason, cast to list explicitly:

result = [list(flatten(sublst[0])) for sublst in big_list]

Upvotes: 5

Related Questions