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?.
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]
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
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
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]
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.
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):])
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?.
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]
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
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
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]
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.
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):])