4

Suppose I have a list of iterators:

iterators = [ iter([2, 3, 4]), iter([7, 9]), iter([-1, -2, -3, -4]) ]

How can I create a new iterator using the above iterators such that at each step it will randomly select one of the iterators present in the list (which are not yet finished) and output the next element from that? It should keep outputting elements until all the iterators have finished.

So, for example, if the new iterator is it_combined, I may get the following output when trying to iterate on it

>>> for it in it_combined:
...     print(it, end=" ")
...
2 7 3 -1 4 -2 -3 -4 9
Arun
  • 114
  • 1
  • 9
  • 1
    Are you able to create iterators in general? Have you seen the examples in the `itertools` standard library module documentation? – Karl Knechtel Jan 25 '22 at 13:30
  • @KarlKnechtel I have read about iterators from [this](https://docs.python.org/3/tutorial/classes.html#iterators) page, but I haven't gone through `itertools` library module documentation. I guess I'll do it now – Arun Jan 25 '22 at 13:36
  • 1
    I was asking because the answers now given are straightforward for those who know the relevant techniques. Sometimes people do know these things, and just need to be given a push to try. If you learned something new from these answers, though, so much the better. – Karl Knechtel Jan 25 '22 at 13:37

2 Answers2

5

You can use random.choice to randomly select an iterator from a list. You then have to make sure to delete this iterator from the list once you've exhausted it.

You can use a generator to implement the merging / sampling of your iterators:

import random

random.seed(42)


def combine_iterators(iterators):
    while iterators:
        it = random.choice(iterators)
        try:
            yield next(it)
        except StopIteration:
            iterators.remove(it)


merged_iterator = combine_iterators(
    [iter([2, 3, 4]), iter([7, 9]), iter([-1, -2, -3, -4])]
)
for x in merged_iterator:
    print(x, end=" ")

Outputs:

-1 2 3 -2 7 4 9 -3 -4
tlgs
  • 643
  • 6
  • 16
  • 2
    Unfortunately, the "simple" approach is not equivalent. It will not preserve the relative order of elements from the same iterator. – Karl Knechtel Jan 25 '22 at 13:41
  • Ah, of course! You're right. – tlgs Jan 25 '22 at 13:42
  • @tlgs have added a variant, feel free to roll back if you don't like it. as it's basically the same as your code didn't think it was worth another "answer" – Sam Mason Jan 25 '22 at 18:56
0

Does this fit your needs?

Select a random iterator and print one of its values. Once that iterator throws its exception, remove it from the list. Continue until all iterators are gone.

import random
iterators = [ iter([2, 3, 4]), iter([7, 9]), iter([-1, -2, -3, -4]) ]

while len(iterators) > 0:
    which = random.randint(0, len(iterators)-1)
    it = iterators[which]
    try:
        print(next(it))
    except Exception:
        del iterators[which]
K. Shores
  • 875
  • 1
  • 18
  • 46
  • 1
    Welcome back to Stack Overflow. As a refresher, please read [answer], and try to explain the approach you take in your code to solve the problem. This is not a code-writing service; the goal is to *answer a question*, not to satisfy the OP's code requirement. – Karl Knechtel Jan 25 '22 at 13:38