How to peek at the next value in a Javascript Iterator

Viewed 1304

Let's say I have an iterator:

function* someIterator () {
    yield 1;
    yield 2;
    yield 3;
}

let iter = someIterator();

... that I look at the next element to be iterated:

let next = iter.next(); // {value: 1, done: false}

... and I then use the iterator in a loop:

for(let i of iterator)
    console.log(i); 
// 2
// 3

The loop will not include the element looked at. I wish to see the next element while not taking it out of the iteration series.

In other words, I wish to implement:

let next = peek(iter); // {value: 1, done: false}, or alternatively just 1

for(let i of iterator)
    console.log(i); 
// 1
// 2
// 3 

... and I wan't to do it without modifying the code for the iterable function.

What I've tried is in my answer. It works (which is why I made it an answer), but I worry that it builds an object that is more complex than it has to be. And I worry that it will not work for cases where the 'done' object is something different than { value = undefined, done = true }. So any improved answers are very much welcome.

2 Answers

Instead of a peek function, I built a peeker function that calls next, removing the element from the iterator, but then adds it back in by creating an iterable function that first yields the captured element, then yields the remaining items in the iterable.

function peeker(iterator) {
    let peeked = iterator.next();
    let rebuiltIterator = function*() {
        if(peeked.done)
            return;
        yield peeked.value;
        yield* iterator;
    }
    return { peeked, rebuiltIterator };
}

function* someIterator () { yield 1; yield 2; yield 3; }
let iter = someIterator();
let peeked = peeker(iter);

console.log(peeked.peeked);
for(let i of peeked.rebuiltIterator())
    console.log(i);

Just a bit different idea is to use wrapper that makes an iterator kind of eagier.

function peekable(iterator) {
  let state = iterator.next();

  const _i = (function* (initial) {
    while (!state.done) {
      const current = state.value;
      state = iterator.next();
      const arg = yield current;
    }
    return state.value;
  })()

  _i.peek = () => state;
  return _i;
}

function* someIterator () { yield 1; yield 2; yield 3; }
let iter = peekable(someIterator());

let v = iter.peek();
let peeked = iter.peek();
console.log(peeked.value);

for (let i of iter) {
  console.log(i);
}

Related