1

How do I yield every alternate element from a generator?

The obvious method doesn't seem to work:

>>> generator[::2]
TypeError: 'generator' object is not subscriptable

Sure you could write code that achieves the same purpose:

x = 0
for item in generator:
    if x % 2 == 0:
        do_something(item)
    x += 1

But Python is bad at math in tight loops like this one and I'm eager to find how this could be done better.

Krish
  • 1,044
  • 9
  • 20
  • 1
    `Python is bad at math in tight loops` - could you explain that a bit? `could be done better.` - better how? – wwii Oct 18 '20 at 21:15
  • 5
    Look at [`itertools.islice`](https://docs.python.org/3.8/library/itertools.html#itertools.islice). You can do `islice(, 0, None, 2)` for example. – Andrej Kesely Oct 18 '20 at 21:17
  • When you write your own generator you have complete control over which values are yielded, so I do not see what is the problem you are facing here – sai Oct 18 '20 at 21:18
  • What do you mean by "done better"? – BrenBarn Oct 18 '20 at 21:20
  • 1
    Strongly recommend `islice` but you could also consume one element from the generator inside the loop like so: `_ = next(generator)` – Pranav Hosangadi Oct 18 '20 at 21:25
  • 3
    @PranavHosangadi You don't need to bother assigning the return value. `while True: next(generator); yield next(generator)`. – chepner Oct 18 '20 at 21:30
  • @chepner I thought so too, but [my quick test](https://i.stack.imgur.com/XwhDS.png) seemed to [indicate otherwise](https://i.stack.imgur.com/VnLEE.png). I realize now it's prob because it was an interactive terminal :-/ – Pranav Hosangadi Oct 18 '20 at 21:35

2 Answers2

2

This kind of approach can be used to deliver only those values following any kind of repetitive index pattern:

def yield_every_other(g):
    from itertools import compress, cycle
    return compress(g, cycle([True, False]))

Then, e.g.,

>>> for x in yield_every_other(2*i for i in range(20)):
...     print(x)

displays

0
4
8
12
16
20
24
28
32
36
Tim Peters
  • 67,464
  • 13
  • 126
  • 132
0

You can use enumerate to make the second option cleaner:

for i, item in enumerate(generator):
    if i % 2 == 0:
        do_something(item)
wjandrea
  • 28,235
  • 9
  • 60
  • 81