Accounting for alternative values ​​in iterations

I am given several iterations, I need to produce all values ​​in the first iterable and then everything in the second, ..., all values ​​in the last iterable.

Example :

for i in alternate('abcde','fg','hijk'): 
      print(i,end=' ')

      

Values ​​are expected to be received

a f h b g i c

      

I know to print all characters in a sequence like

'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'

      

Here is the code:

def alternate(*args):
    for iterable in args:
        for item in iterable:
            yield item

      

But how can I give them as an alternative?

+3


source to share


2 answers


If you want to end up in the shortest list then use zip

:

Create an iterator that combines elements from each of the repeating ones.

def alternate(*args):
    # note: python 2 - use izip_longest
    for iterable in zip(*args):
        for item in iterable:
            if item is not None:
                yield item

      

If you want to use all elements, use itertools.izip_longest :

Make an iterator that combines elements from each of the repeating ones. If the iterators are of uneven length, the missing values ​​are filled with fillvalue.

def alternate(*args):
    # note: python 2 - use izip_longest
    for iterable in itertools.zip_longest(*args):
        for item in iterable:
            if item is not None:
                yield item

      

Note that it is missing values None

(can be changed with fillvalue

).



Without itertools

:

def alternate(*args):
    max_len = max(map(len, args))
    for index in range(max_len):
        for lst in args:
            try:
                yield lst[index]
            except IndexError:
                continue

      

To stop the first "missing" element:

def alternate(*args):
    index = 0
    while True:
        for lst in args:
            try:
                yield lst[index]
            except IndexError:
                return
        index += 1

      

And if you use generators (or iterators) you get StopIteration

when one of them is executed, so you can use iter

to create a generic function:

def alternate(*args):
    iters = [iter(lst) for lst in args]
    while True:
        for itr in iters:
            try:
                yield next(itr)
            except StopIteration:
                return

      

+5


source


This will do what you want. It works for any input, it won't fire if one of the elements None

. It uses built-in module itertools

and will work on python 3.x and python 2.7 with python built-in modules only. It is based on roundrobin

from the Python documentation itertools

:

from itertools import cycle

def roundrobin_shortest(*iterables):
    "roundrobin_shortest('ABCD', 'EF', 'GHI') --> A E G B F H C"
    nexts = cycle(iter(it).__next__ for it in iterables)
    while True:
        try:
            for inext in nexts:
                yield inext()
        except StopIteration:
            break

      

This version works basically the same, but is a little shorter:

from itertools import chain, cycle

def roundrobin_shortest(*iterables):
    "roundrobin_shortest('ABCD', 'EF', 'GHI') --> A E G B F H C"
    nexts = cycle(iter(it).__next__ for it in iterables)
    yield from chain.from_iterable(inext() for inext in nexts)

      

And the version without itertools:



def roundrobin_shortest(*iterables):
    "roundrobin_shortest('ABCD', 'EF', 'GHI') --> A E G B F H C"
    items = [iter(item) for item in iterables]
    while True:
       try:
           for it in items:
               yield next(it)
       except StopIteration:
           break

      

You can use any of them, for example:

>>> for i in roundrobin_shortest('abcde','fg','hijk'): 
...     print(i, end=' ')
...
a f h b g i c 

      

Or here's a simpler one-liner

>>> from itertools import chain, cycle
>>> iters = ('abcde','fg','hijk')
>>> res = chain.from_iterable(j() for j in cycle(iter(j).__next__ for j in iters))
>>> print(' '.join(res))
a f h b g i c

      

0


source







All Articles