Expanding Nested lists in Python without fully flattening

Question:

Suppose I have a list with nested lists such of strings such as:

items = ['Hello', ['Ben', 'Chris', 'Linda'], '! The things you can buy today are', ['Apples', 'Oranges']]

I want a list of strings that combine and flatten the nested lists into all possibilities such that the result is:

new_list = ['Hello Ben ! The things you can buy today are Apples',
            'Hello Ben ! The things you can buy today are Oranges',
            'Hello Chris ! The things you can buy today are Apples',
            'Hello Chris ! The things you can buy today are Oranges',
            'Hello Linda ! The things you can buy today are Apples',
            'Hello Linda ! The things you can buy today are Oranges',]

I’ve been looking through itertools documentation and nothing quite works as expected. I don’t want to hard code iterations because this items list can range in number of items as well as number of nested lists.

For example:

list(itertools.chain(*items))

Will flatten the list but it splits up individual characters in the string items. Part of the challenge is that some items in the list are strings, and others are additional lists. Would appreciate any help. Thanks

Asked By: Alison LT

||

Answers:

You need itertools.product()

Here is it in action –

>>> items = [['Hello'], ['Ben', 'Chris', 'Linda']]
>>> list(itertools.product(*items))
[('Hello', 'Ben'), ('Hello', 'Chris'), ('Hello', 'Linda')]

Since itertools.product() takes list of lists as an input, so some transformation is needed in your code to convert 'Hello' to ['Hello']

import itertools
items = ['Hello', ['Ben', 'Chris', 'Linda'], '! The things you can buy today are', ['Apples', 'Oranges']]
new_items = itertools.product(*[item if isinstance(item, list) else [item] for item in items])
new_list = [' '.join(x) for x in new_items]

new_list:

['Hello Ben ! The things you can buy today are Apples',
 'Hello Ben ! The things you can buy today are Oranges',
 'Hello Chris ! The things you can buy today are Apples',
 'Hello Chris ! The things you can buy today are Oranges',
 'Hello Linda ! The things you can buy today are Apples',
 'Hello Linda ! The things you can buy today are Oranges']
Answered By: Jay

you could solve with backtracking:

def test(itm):
    n = len(itm)
    results = []
    def dfs(idx, res):
        if idx == n:
            results.append(res.copy())
            return
        if isinstance(itm[idx], list):
            for d in itm[idx]:
                res.append(d)
                dfs(idx+1, res)
                res.pop()
        else:
                res.append(itm[idx])
                dfs(idx+1, res)
                res.pop()

    dfs(0, [])
    return results

output:

test(items)

[['Hello', 'Ben', '! The things you can buy today are', 'Apples'],
 ['Hello', 'Ben', '! The things you can buy today are', 'Oranges'],
 ['Hello', 'Chris', '! The things you can buy today are', 'Apples'],
 ['Hello', 'Chris', '! The things you can buy today are', 'Oranges'],
 ['Hello', 'Linda', '! The things you can buy today are', 'Apples'],
 ['Hello', 'Linda', '! The things you can buy today are', 'Oranges']]
Answered By: amirhm