9

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())?

kleinfreund
  • 6,546
  • 4
  • 30
  • 60
Armeen Moon
  • 18,061
  • 35
  • 120
  • 233
  • possible duplicate of [ES6 reverse iterate an array using for..of](http://stackoverflow.com/q/32444463/1048572) or [es6 map/set iterate backwards](http://stackoverflow.com/q/32498034/1048572) – Bergi Mar 10 '16 at 17:40

4 Answers4

2

The values() returns an iterator object and it is not possible to iterate them backwards, because JavaScript iterator objects could be infinite. For example, consider this

function * Counter() {
    "use strict";
    var i = 0;
    while (1) {
        yield i++;
    }
}

Now, you can create an iterator with Counter() and that will never end. So going backwards is not an option with iterators, generally.


If you badly need something like backIterator, then you have to maintain the values produced by the iterator and then move back and forth based on the next calls.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
2

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.

kleinfreund
  • 6,546
  • 4
  • 30
  • 60
0

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 };
    }
  };
}
Neil
  • 1,928
  • 19
  • 14
-1

I'm afraid there's no JS method to do that, however nobody stops you from saving the previous value in a variable and reading from it.

Simone
  • 20,302
  • 14
  • 79
  • 103