create list with elements which are sublists that alternate between elements in sublists from two other lists

Question:

The title is a bit weird but essentially I need to take two lists:

list1 = [['1', '1', '1', '1'], ['2', '2', '2', '2'], ['3', '3', '3', '3']]
list2 = [['a', 'a', 'a'], ['b', 'b', 'b']]

and then alternate between the elements in the sub-lists of the two lists and create sub-lists as elements in a new list whose sub-lists are the alternated elements from above

list3 = [['1', 'a', '1', 'a', '1', 'a', '1'], ['1', 'b', '1', 'b', '1', 'b', '1'], ['2', 'a', '2', 'a', '2', 'a', '2'], ... ]

right now my code is:

def foo(array1, array2):
    i = 1
    for sublist1 in array1:
        for sublist2 in array2:
            for val in sublist2:
                sublist1.insert(i, val)
                i += 2
        i = 1
    return array1

and I’m getting the output:

[['1', 'a', '1', 'a', '1', 'a', '1', 'b', 'b', 'b'], ['2', 'a', '2', 'a', '2', 'a', '2', 'b', 'b', 'b'], ['3', 'a', '3', 'a', '3', 'a', '3', 'b', 'b', 'b']]

the thing is that I’m working with much smaller lists as a proof of concept right now but the final algorithm will need to be able to do this for lists with millions of sub-lists.

Asked By: crxunch

||

Answers:

I’d use itertools for the task:

from itertools import product, zip_longest

list1 = [["1", "1", "1", "1"], ["2", "2", "2", "2"], ["3", "3", "3", "3"]]
list2 = [["a", "a", "a"], ["b", "b", "b"]]

out = []
for c in product(list1, list2):
    out.append([v for c in zip_longest(*c) for v in c if v is not None])

print(out)

Prints:

[
    ["1", "a", "1", "a", "1", "a", "1"],
    ["1", "b", "1", "b", "1", "b", "1"],
    ["2", "a", "2", "a", "2", "a", "2"],
    ["2", "b", "2", "b", "2", "b", "2"],
    ["3", "a", "3", "a", "3", "a", "3"],
    ["3", "b", "3", "b", "3", "b", "3"],
]

Note: If there’s None in any of your sub-lists, use other fillvalue= in itertools.zip_longest

Answered By: Andrej Kesely

You can use a variation on itertools’ roundrobin:

from itertools import cycle, islice
def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    num_active = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while num_active:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            # Remove the iterator we just exhausted from the cycle.
            num_active -= 1
            nexts = cycle(islice(nexts, num_active))

   
out = [list(roundrobin(a, b)) for a, b in zip(list1, list2)]

Output:

[['1', 'a', '1', 'a', '1', 'a', '1'], ['2', 'b', '2', 'b', '2', 'b', '2']]
Answered By: mozway

I’d use product to get the Cartesian product of the two lists, and then zip and chain them:

result = [list(chain.from_iterable(zip(*p))) for p in product(list1, list2)]
Answered By: Mureinik

use deepcopy:

from copy import deepcopy

def foo(list1, list2):
    ans = []
    for L1 in list1:
        for L2 in list2:
            L3 = deepcopy(L1)
            for i, val in enumerate(L2):
                L3.insert(i * 2 + 1, val)
            ans.append(L3)
    return ans