How to interleave two lists of different length?

Question:

I want to write a function twolists which gives results like:

outcome = twolists([ ], ['w', 'x', 'y', 'z'])
print(outcome)
['w', 'x', 'y', 'z']

outcome = twolists([0, 1], ['w', 'x'])
print(outcome)
[0, 'w', 1, 'x']
 
outcome = twolists([0, 1], ['w', 'x', 'y', 'z'])
print(outcome)
[0, 'w', 1, 'x', 'y', 'z']

outcome = twolists([0, 1, 2, 3], ['w', 'x'])
print(outcome)
[0, 'w', 1, 'x', 2, 3]

I have this so far:

def twolists(list1, list2): # don't forget to return final_list
    alt_list = []
    a1 = len(list1)
    a2 = len(list2)
    
    for i in range(# ? ):
        # append one thing from list1 to alt_list - How?
        # append one thing from list2 to alt_list - How?

How can I complete the code?


If you instead want to intersperse elements from the shorter list with roughly even spacing, see How to elegantly interleave two lists of uneven length in python?.

Asked By: FelixF

||

Answers:

This composes a list comprehension using zip_longest from itertools (which is part of the standard library) to interleave items from both lists into a tuple, which by default uses None as the fillvalue.

This also uses chain also from itertools to flatten the list.

Finally it filters the None items from the list:

from itertools import chain, zip_longest
def twolists(l1, l2):
    return [x for x in chain(*zip_longest(l1, l2)) if x is not None]

Or as recommended from @EliKorvigo, use itertools.chain.from_iterable for iterating lazily:

def twolists(l1, l2):
    return [x for x in chain.from_iterable(zip_longest(l1, l2)) if x is not None]

Testing

In [56]: twolists([0, 1], ['w', 'x'])
Out[56]: [0, 'w', 1, 'x']

In [57]: twolists([0, 1], ['w', 'x', 'y', 'z'])
Out[57]: [0, 'w', 1, 'x', 'y', 'z']

In [74]: twolists([0, 1, 2, 3], ['w', 'x'])
Out[74]: [0, 'w', 1, 'x', 2, 3]
Answered By: salparadise
def twolists(list1, list2):
    newlist = []
    a1 = len(list1)
    a2 = len(list2)

    for i in range(max(a1, a2)):
        if i < a1:
            newlist.append(list1[i])
        if i < a2:
            newlist.append(list2[i])

    return newlist
Answered By: John Gordon

Here’s a solution that deals in iterators. The advantage to this is that it will work with any iterable data structure, not just lists.

def twolists(list1, list2):
    result = []
    iter1 = iter(list1)
    iter2 = iter(list2)
    try:
        while True:
            result.append(next(iter1))
            result.append(next(iter2))
    except StopIteration:
        # This exception will be raised when either of the iterators
        # hits the end of the sequence.
        pass
    # One of the lists is exhausted, but not both of them. We need
    # to finish exhausting the lists.
    try:
        while True:
            result.append(next(iter1))
    except StopIteration:
        pass
    try:
        while True:
            result.append(next(iter2))
    except StopIteration:
        pass
    return result
Answered By: Silvio Mayolo

If you don’t care whether your original lists (a and b in the following example) change, you can use the following snippet:

def twolists(a, b):
    result = []
    while len(a) > 0:
        result.append(a.pop(0))
        if len(b) > 0:
            result.append(b.pop(0))
    result += a + b
    return result

twolists([ ], ['w', 'x', 'y', 'z'])
print(outcome)

outcome = twolists([0, 1], ['w', 'x'])
print(outcome)

outcome = twolists([0, 1], ['w', 'x', 'y', 'z'])
print(outcome)

outcome = twolists([0, 1, 2, 3], ['w', 'x'])
print(outcome)

Produces the following output:

['w', 'x', 'y', 'z']
[0, 'w', 1, 'x']
[0, 'w', 1, 'x', 'y', 'z']
[0, 'w', 1, 'x', 2, 3]
Answered By: Damodar Dahal

A basic approach:

You could zip() the lists normally, and append the rest of the biggest list if both lists are not the same size:

def two_lists(lst1, lst2):
    result = []

    for pair in zip(lst1, lst2):
        result.extend(pair)

    if len(lst1) != len(lst2):
        lsts = [lst1, lst2]
        smallest = min(lsts, key = len)
        biggest = max(lsts, key = len)
        rest = biggest[len(smallest):]
        result.extend(rest)

    return result

Which works as follows:

>>> print(two_lists([], ['w', 'x', 'y', 'z']))
['w', 'x', 'y', 'z']
>>> print(two_lists([0, 1], ['w', 'x']))
[0, 'w', 1, 'x']
>>> print(two_lists([0, 1], ['w', 'x', 'y', 'z']))
[0, 'w', 1, 'x', 'y', 'z']
>>> print(two_lists([0, 1, 2, 3], ['w', 'x']))
[0, 'w', 1, 'x', 2, 3]

Another possible approach:

You could also use collections.deque to convert the lists to deque() objects beforehand, and pop off the beginning of each one with popleft(), until one of the objects is empty. Then you could append the rest of the list that is not yet empty.

Here is an example:

def two_lists2(lst1, lst2):
    result = []

    fst, snd = deque(lst1), deque(lst2)

    while fst and snd:
        result.append(fst.popleft())
        result.append(snd.popleft())

    rest = leftover(fst, snd)
    if rest:
        result.extend(rest)

    return result

def leftover(x, y):
    if x and not y:
        return x

    elif y and not x:
        return y

    return None

Note: Both of the approaches are O(n) time, which is expected for this kind of problem.

Answered By: RoadRunner
def CombineLists(lst1, lst2):
   return [item for x in zip(lst1,lst2) for item in x] + /
         (lst2[len(lst1):] if len(lst2)>len(lst1) else lst1[len(lst2):])
Answered By: Tomer
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.