8

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?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
codefreak
  • 309
  • 2
  • 4
  • 15

9 Answers9

6

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"))
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • What if sequences are of unequal length? – Ashwini Chaudhary Jan 11 '14 at 13:11
  • @thefourtheye I am making a function that takes variable arguments each time. How to do for that??? – codefreak Jan 11 '14 at 13:12
  • @AshwiniChaudhary In that case, [zip](http://docs.python.org/3/library/functions.html#zip) terminates after the first sequence reached it’s end. You can use [``itertools.zip_longest``](http://docs.python.org/3/library/itertools.html#itertools.zip_longest) to pad with ``None`` (or any other value). – Jonas Schäfer Jan 11 '14 at 13:12
  • @JonasWielicki We don't pad with `None`(or any other value) in `roundrobin`, we need the item from next iterable in that case. – Ashwini Chaudhary Jan 11 '14 at 13:13
4

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")))
Bar
  • 2,736
  • 3
  • 33
  • 41
Andrea de Marco
  • 827
  • 5
  • 9
3

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)

Abhijit
  • 62,056
  • 18
  • 131
  • 204
2

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")))
Mike T
  • 41,085
  • 18
  • 152
  • 203
1

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)  
Ajasja
  • 809
  • 1
  • 16
  • 21
Neeraj Sharma
  • 1,322
  • 10
  • 9
1

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

0

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.

ignorant
  • 1,390
  • 1
  • 10
  • 14
0

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']
sage byte
  • 1
  • 1
-2

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]

  • Please explain why your answer should provide better information than the other answers already posted to this question. If you keep being convinced it does, please edit your answer and fix the code block formatting. – jpeg May 29 '19 at 13:59