7

I'm quite new to Python and I am still having a hard time actually using the language itself into my program. Here's what I have so far:

# Purpose: 'twolists' = takes 2 lists, & returns a new list containing
# alternating elements of lists. 
# Return = final_list
# Parameter = list1, list2

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?

Now the program is supposed to yield outputs like these:

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]
Tim Pietzcker
  • 297,146
  • 54
  • 452
  • 522
FelixF
  • 91
  • 1
  • 5
  • Does this answer your question? [Pythonic way to combine two lists in an alternating fashion?](https://stackoverflow.com/questions/3678869/pythonic-way-to-combine-two-lists-in-an-alternating-fashion) – Vaidøtas I. Jan 04 '21 at 20:19

6 Answers6

11

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]
salparadise
  • 4,971
  • 1
  • 22
  • 30
  • Look at the desired output. This is completely different from what was requested. – Silvio Mayolo Jan 11 '18 at 04:57
  • @SilvioMayolo very well, I have edited and addressed the interleaving requirement. – salparadise Jan 11 '18 at 05:03
  • You beat me to it. Though, instead of `chain(*zip_longest(...))` you'd better use `chain.from_iterable(zip_longest(...))`. That starred expansion kills all the laziness early on. – Eli Korvigo Jan 11 '18 at 05:23
  • another (more functional) variant could use the [filter](https://docs.python.org/3/library/functions.html#filter) builtin function instead of a list comprehension: `filter(lambda i: i is not None, chain.from_iterable(zip_longest(a, b)))`. And it is even more lazy ;) – Tryph Jan 06 '21 at 10:59
10
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
John Gordon
  • 19,454
  • 6
  • 24
  • 42
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.

RoadRunner
  • 23,173
  • 5
  • 28
  • 59
1
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):])
Syscall
  • 16,959
  • 9
  • 22
  • 41
Tomer
  • 11
  • 1
  • While this code snippet may be the solution, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-‌​code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – dan1st Mar 08 '21 at 16:57
  • First we're merging 2 equal lists by iterating over zip and then adding items from the longest list. – Tomer Mar 10 '21 at 11:25
-1

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
Silvio Mayolo
  • 24,199
  • 3
  • 34
  • 65
-1

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]
Damodar Dahal
  • 519
  • 4
  • 15
  • 1
    This is (1) awfully effectful and (2) has quadratic time complexity for no good reason. This task is naturally linear in time, why make a quadratic monster out of it? – Eli Korvigo Jan 11 '18 at 05:34
  • @EliKorvigo, That was a needless snipe. You don't like the solution, fair enough, call that out. Questioning the motive behind posting the answer, is poor form. – Neowizard Dec 14 '20 at 00:17
  • @EliKorvigo, if you look at the solution carefully, you'll realize that it has a linear time complexity, `O(len(a) + len(b))`. Also, please use professional language for communication on Stackoverflow. – Damodar Dahal Feb 22 '21 at 23:09
  • Also, it's very effectful. It matches all test cases provided by the questioner, and a side effect is explained in the answer above the code snippet. – Damodar Dahal Feb 22 '21 at 23:11