Round Robin method of mixing of two lists in python

Question:

If input is

round_robin(range(5), "hello")

I need output as

[0, 'h', 1, 'e', 2, 'l', 3, 'l', 4, 'o']

I tried

def round_robin(*seqs):
list1=[]
length=len(seqs)
list1= cycle(iter(items).__name__ for items in seqs)
while length:
    try:
        for x in list1:
            yield x
    except StopIteration:
        length -= 1

pass

but it gives error as

AttributeError: 'listiterator' object has no attribute '__name__'

How to modify the code to get the desired output?

Asked By: codefreak

||

Answers:

You can use zip function and then flatten the result with list comprehension, like this

def round_robin(first, second):
    return[item for items in zip(first, second) for item in items]
print round_robin(range(5), "hello")

Output

[0, 'h', 1, 'e', 2, 'l', 3, 'l', 4, 'o']

zip function groups the values from both the iterables, like this

print zip(range(5), "hello") # [(0, 'h'), (1, 'e'), (2, 'l'), (3, 'l'), (4, 'o')]

We take each and every tuple and flatten it out with list comprehension.

But as @Ashwini Chaudhary suggested, use roundrobin receipe from the docs

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

print list(roundrobin(range(5), "hello"))
Answered By: thefourtheye

You can leverage itertools.chain (to unwrap the tuples) with itertools.izip (to transpose the elements in order to create an interleaving pattern) to create your result

>>> from itertools import izip, chain
>>> list(chain.from_iterable(izip(range(5), "hello")))
[0, 'h', 1, 'e', 2, 'l', 3, 'l', 4, 'o']

If the strings are of unequal length, use izip_longest with a pad value (preferably empty string)

Answered By: Abhijit

You could find a series of iteration recipes here: http://docs.python.org/2.7/library/itertools.html#recipes

from itertools import islice, cycle


def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).next for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))


print list(roundrobin(range(5), "hello"))

EDIT: Python 3

https://docs.python.org/3/library/itertools.html#itertools-recipes

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:
            num_active -= 1
            nexts = cycle(islice(nexts, num_active))

print(list(roundrobin(range(5), "hello")))
Answered By: Andrea de Marco

list(roundrobin(‘ABC’, ‘D’, ‘EF’))

Output : [‘A’, ‘D’, ‘E’, ‘B’, ‘F’, ‘C’]

def roundrobin(*iterables):
    sentinel = object()
    from itertools import chain
    try:
        from itertools import izip_longest as zip_longest
    except:
        from itertools import zip_longest 
    return (x for x in chain(*zip_longest(fillvalue=sentinel, *iterables)) if x is not sentinel)  
Answered By: Neeraj Sharma

For anyone looking for Python 3, use this

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))

The difference is that Python 3’s iterator has __next__() instead of next().
https://docs.python.org/3/library/itertools.html#recipes

Answered By: user9474006

A mixture of the two itertools roundrobin recipes for Python 2 and Python 3 looks like this:

from itertools import islice, cycle

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    num_active = len(iterables)
    try:
        iter([]).__next__  # test attribute
        nexts = cycle(iter(it).__next__ for it in iterables)
    except AttributeError:  # Python 2 behavior
        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))

print(list(roundrobin(range(5), "hello")))
Answered By: Mike T

from itertools import cycle

A = [[1,2,3],[4,5,6],[7]]

B = [[8],[9,10,11],[12,13]]

for p in A:

max1 = len(p) if  max1 <len(p) else max1

for p in B:

max1 = len(p) if  max1 <len(p) else max1

i = len(A)

j = 0

C = []

list_num = cycle(k for k in range(i))

for x in list_num:

j += 1

if j == i*3:

    break


if A[x]:

    C.append(A[x].pop(0))

if B[x]:

    C.append(B[x].pop(0)) 

Output=====> [1, 8, 4, 9, 7, 12, 2, 5, 10, 13, 3, 6, 11]

Answered By: Ankush Ukirde

Similar solution (for python 3)

from itertools import chain, repeat, zip_longest

stuff = (repeat("A", 2), repeat("B", 3), repeat("C", 5))


# Round robin through iterables
[i for i in chain(*zip_longest(*stuff)) if i is not None]

Got back

['A', 'B', 'C', 'A', 'B', 'C', 'B', 'C', 'C', 'C']

Warning: This discards None. You could replace with another stand-in, but it is not a general solution for all use cases.

Answered By: ignorant

for python3, you can use more-itertools,
like this

import more_itertools as mit
list(mit.interleave(range(5), "hello"))

Output

[0, 'h', 1, 'e', 2, 'l', 3, 'l', 4, 'o']

or use pytoolz

import toolz.itertoolz as ti
list(ti.interleave((range(5), "hello")))

Output

[0, 'h', 1, 'e', 2, 'l', 3, 'l', 4, 'o']
Answered By: sage byte
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.