In Python, is there a good way to interleave two lists of the same length?
Say I’m given [1,2,3] and [10,20,30]. I’d like to transform those into [1,10,2,20,3,30].
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Having posted the question, I’ve realised that I can simply do the following:
[val for pair in zip(l1, l2) for val in pair]
where l1 and l2 are the two lists.
If there are N lists to interleave, then
lists = [l1, l2, ...] [val for tup in zip(*lists) for val in tup]
Method 2
For Python>=2.3, there’s extended slice syntax:
>>> a = [0, 2, 4, 6, 8] >>> b = [1, 3, 5, 7, 9] >>> c = a + b >>> c[::2] = a >>> c[1::2] = b >>> c [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
The line c = a + b is used as a simple way to create a new list of exactly the right length (at this stage, its contents are not important). The next two lines do the actual work of interleaving a and b: the first one assigns the elements of a to all the even-numbered indexes of c; the second one assigns the elements of b to all the odd-numbered indexes of c.
Method 3
Given
a = [1, 2, 3] b = [10, 20, 30] c = [100, 200, 300, 999]
Code
Assuming lists of equal length, you can get an interleaved list with itertools.chain and zip:
import itertools list(itertools.chain(*zip(a, b))) # [1, 10, 2, 20, 3, 30]
Alternatives
More generally with unequal lists, use zip_longest (recommended):
[x for x in itertools.chain(*itertools.zip_longest(a, c)) if x is not None] # [1, 100, 2, 200, 3, 300, 999]
Many lists can safely be interleaved:
[x for x in itertools.chain(*itertools.zip_longest(a, b, c)) if x is not None] # [1, 10, 100, 2, 20, 200, 3, 30, 300, 999]
A library that ships with the roundrobin itertools recipe, interleave and interleave_longest.
import more_itertools list(more_itertools.roundrobin(a, b)) # [1, 10, 2, 20, 3, 30] list(more_itertools.interleave(a, b)) # [1, 10, 2, 20, 3, 30] list(more_itertools.interleave_longest(a, c)) # [1, 100, 2, 200, 3, 300, 999]
yield from
Finally, for something interesting in Python 3 (though not recommended):
list(filter(None, ((yield from x) for x in zip(a, b)))) # [1, 10, 2, 20, 3, 30] list([(yield from x) for x in zip(a, b)]) # [1, 10, 2, 20, 3, 30]
+Install using pip install more_itertools
Method 4
I needed a way to do this with lists of different sizes which the accepted answer doesn’t address.
My solution uses a generator and its usage looks a bit nicer because of it:
def interleave(l1, l2):
iter1 = iter(l1)
iter2 = iter(l2)
while True:
try:
if iter1 is not None:
yield next(iter1)
except StopIteration:
iter1 = None
try:
if iter2 is not None:
yield next(iter2)
except StopIteration:
iter2 = None
if iter1 is None and iter2 is None:
raise StopIteration()
And its usage:
>>> a = [1, 2, 3, 4, 5] >>> b = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] >>> list(interleave(a, b)) [1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 'f', 'g'] >>> list(interleave(b, a)) ['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 'g']
Method 5
Alternative:
>>> l1=[1,2,3] >>> l2=[10,20,30] >>> [y for x in map(None,l1,l2) for y in x if y is not None] [1, 10, 2, 20, 3, 30]
This works because map works on lists in parallel. It works the same under 2.2. By itself, with None as the called functions, map produces a list of tuples:
>>> map(None,l1,l2,'abcd') [(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]
Then just flatten the list of tuples.
The advantage, of course, is map will work for any number of lists and will work even if they are different lengths:
>>> l1=[1,2,3] >>> l2=[10,20,30] >>> l3=[101,102,103,104] >>> [y for x in map(None,l1,l2,l3) for y in x if y in not None] [1, 10, 101, 2, 20, 102, 3, 30, 103, 104]
Method 6
I like aix’s solution best. here is another way I think should work in 2.2:
>>> x=range(3) >>> x [0, 1, 2] >>> y=range(7,10) >>> y [7, 8, 9] >>> sum(zip(x,y),[]) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate list (not "tuple") to list >>> sum(map(list,zip(x,y)),[]) [0, 7, 1, 8, 2, 9]
and one more way:
>>> a=[x,y] >>> [a[i][j] for j in range(3) for i in (0,1)] [0, 7, 1, 8, 2, 9]
and:
>>> sum((list(i) for i in zip(x,y)),[]) [0, 7, 1, 8, 2, 9]
Method 7
A funny approach is to use heapq.merge with the position in the final list as key:
from heapq import merge from itertools import count a = [1,2,3] b = [10,20,30] counter = count() res = list(merge(a, b, key=lambda x: next(counter))) print(res)
Output
[1, 10, 2, 20, 3, 30]
For multiple list, you can just unpack them:
from heapq import merge from itertools import count a = [1, 2, 3] b = [10, 20, 30] c = [11, 21, 31] counter = count() res = list(merge(*[a, b, c], key=lambda x: next(counter))) print(res)
Output
[1, 10, 11, 2, 20, 21, 3, 30, 31]
Method 8
This is also a way to do it:
list1 = [1, 2, 3] list2 = [10, 20, 30] list(sum(zip(list1, list2), ()))
The idea is similar.
- zip the lists together. (using zip)
- flatten to a tuple (using sum(…, ())
- convert to a list
Method 9
[el for el in itertools.chain(*itertools.izip_longest([1,2,3], [4,5])) if el is not None]
As long as you don’t have None that you want to keep
Method 10
To answer the question’s title of “Interleave multiple lists of the same length in Python”, we can generalize the 2-list answer of @ekhumoro. This explicitly requires that the lists are the same length, unlike the (elegant) solution by @NPE
import itertools
def interleave(lists):
"""Interleave a list of lists.
:param lists: List of lists; each inner length must be the same length.
:returns: interleaved single list
:rtype: list
"""
if len(set(len(_) for _ in lists)) > 1:
raise ValueError("Lists are not all the same length!")
joint = list(itertools.chain(*lists))
for l_idx, li in enumerate(lists):
joint[l_idx::len(lists)] = li
return joint
Examples:
>>> interleave([[0,2,4], [1, 3, 5]]) [0, 1, 2, 3, 4, 5] >>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12]]) [0, 1, 10, 2, 3, 11, 4, 5, 12] >>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14, 15]]) [0, 1, 10, 13, 2, 3, 11, 14, 4, 5, 12, 15] >>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14]]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 10, in interleave ValueError: Lists are not all the same length! >>> interleave([[0,2,4]]) [0, 2, 4]
Method 11
Too late to the party, and there is plenty of good answers but I would also like to provide a simple solution using extend() method:
list1 = [1, 2, 3]
list2 = [10, 20, 30]
new_list = []
for i in range(len(list1)):
new_list.extend([list1[i], list2[i]])
print(new_list)
Output:
[1, 10, 2, 20, 3, 30]
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0