9

I cannot use itertools

So the coding seems pretty simple, but I'm having trouble thinking of the algorithm to keep a generator running until all iterations have been processed fully.

The idea of the function is to take 2 iterables as parameters like this ...

(['a', 'b', 'c', 'd', 'e'], [1,2,5])

And what it does is yield these values ...

a, b, b, c, c, c, c, c

However, in the event that the second iterable runs out of elements first, the function simply iterates the remaining value one time ...

So the remaining values would be iterated like this:

d, e

def iteration(letters, numbers):
    times = 0
    for x,y in zip(letters, numbers):
        try:
            for z in range(y):
                yield x
        except:
            continue

[print(x) for x in iteration(['a', 'b', 'c', 'd'], [1,2,3])]

I'm having difficulty ignoring the first StopIteration and continuing to completion.

FlyingBumble
  • 195
  • 1
  • 11
  • Well that's where I'm having problems with as well. I don't know how to get the iteration to continue together simultaneously so that a gets the 1 value, and b gets the 2 value. – FlyingBumble May 07 '15 at 23:23
  • what should happen with d and e? – Padraic Cunningham May 07 '15 at 23:25
  • When the iterable with the numbers runs out, the remaining iterables in the first one should be yielded only one time each. – FlyingBumble May 07 '15 at 23:26
  • 2
    If you can't use itertools, read the documentation for the function itertools.zip_longest. There's a snippet of code there that says "Equivalent to" zip_longest. That's your solution, unless you are required to re-invent the wheel. In any case you can't use zip because it stops iterating when one of the iterables is exhausted. – Paul Cornelius May 07 '15 at 23:43

2 Answers2

19

Use a default value of 1 for next so you print the letters at least once:

def iteration(letters, numbers): 
     # create iterator from numbers
    it = iter(numbers)
    # get every letter
    for x in letters:
        # either print in range passed or default range of 1
        for z in range(next(it, 1)):
            yield x

Output:

In [60]: for s in iteration(['a', 'b', 'c', 'd', 'e'], [1,2,5]):
   ....:     print(s)
   ....:     
a
b
b
c
c
c
c
c
d
e
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • Thanks this helped a bunch, and it was easy to understand where I went wrong. I didn't know that you could assign a default value to an iterable if it failed. – FlyingBumble May 07 '15 at 23:31
  • @FlyingBumble, no prob, it was an interesting problem. Yes the second value to next is a default value which will avoid a StopIteration and also allow us to see each string at least once. – Padraic Cunningham May 07 '15 at 23:34
3

Read the documentation for zip(). It says: "zip() should only be used with unequal length inputs when you don’t care about trailing, unmatched values from the longer iterables. If those values are important, use itertools.zip_longest() instead."

Paul Cornelius
  • 9,245
  • 1
  • 15
  • 24