In ES6, there is iterator.next(); is there any way to supply iterator.previous()?

Viewed 2854

Going through the full ES6 Compatibility table. Just got on to Set().

const set = new Set();
set.add('foo');
set.add('baz');

const iterator = set.values();
iterator.next(); // { value: "foo", done: false }
iterator.next(); // { value: "baz", done: false }

Is it possible to write a method similar to iterator.next(), but it iterates backwards instead of forwards (i.e. iterator.previous())?

4 Answers

Although you can’t provide a previous() method to iterators similar to the next() method at this time, the other answers are not correct. You can iterate backwards over any iterable with the ES iteration protocol by changing the next() method of the iterator.

The real culprit is that you need to decide whether you want to iterate forwards or backwards. Currently, you can’t do both at the same time because you have to override the next() method in the iterator to have it work backwards.

Continuing your example with the Set object, you can override the values() method and provide your own iterable iterator like this:

const set = new Set();
set.add('foo');
set.add('baz');

const iterator = set.values();
console.log(iterator.next()); // { value: "foo", done: false }
console.log(iterator.next()); // { value: "baz", done: false }

set.values = function () {
  const reversedContents = Array.from(this);
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      const isDone = reversedContents.length === 0;
      return {
        value: reversedContents.pop(),
        done: isDone
      };
    }
  };
}

const reverseIterator = set.values();
console.log(reverseIterator.next()); // { value: "baz", done: false }
console.log(reverseIterator.next()); // { value: "foo", done: false }

This is not ideal as this creates an array out of the set in order to obtain the last element from the set.

There is nothing to stop you doing this, for example below by wrapping a Set.

You obviously won't be able to iterate over previous() directly, but you could call it manually, or simply switch direction with a call to previous(), and next now iterates in reverse.

values() {
  const items = [...set.values()];
  let index = -1;

  return {
    [Symbol.iterator]() {
      return this;
    },

    next() {
      var item = items[index + 1];
      if (item) {
        return {
          value: item,
          done: false
        };
      }
      return { done: true };
    },

    previous() {
      var item = items[index - 1];
      if (item) {
        return {
          value: item,
          done: false
        };
      }
      return { done: true };
    }
  };
}
Related