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