1

I'm relatively new to generator functions and was wondering if there is a way to get the previous value yielded from the generator function. I know I can use .next() to proceed to the next yield, but I'm wondering if it would be possible to do something like .prev(), which would essentially go back a step.

I was hoping .prev() would work, however, it unfortunately doesn't:

const array = ['a', 'b', 'c'];

function* f(array) {
  let i = 0;
  while(true) {
    yield array[i];
    i = (i+1) % array.length;
  }
}

const seq = f(array);
console.log(seq.next().value); // a
console.log(seq.next().value); // b
console.log(seq.next().value); // c

console.log(seq.next().value); // a

// console.log(seq.prev().value); // c <-- expect 'c'

As I mentioned, I'm inexperienced with generators, and if there is a better data-structure/method to progressively get values (previous and next) I'm open to hearing about those suggestions

Shnick
  • 1,199
  • 1
  • 13
  • 32
  • 2
    Where do you see "prev()" (or anything like it) in the documentation for [Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next)? You can easily write your *OWN* JS prototype object that allows any operation you want. But this particular JS prototype happens to be "forward only". – paulsm4 Sep 28 '19 at 02:24
  • 2
    May be just me but what is the use case for this ? – Code Maniac Sep 28 '19 at 02:26
  • @paulsm4 Hi, yes, I'm aware that `prev()` is no part of the documentation, I was just using to to help explain the idea/concept I'm after – Shnick Sep 28 '19 at 02:30
  • @CodeManiac specifically, I saw [this](https://stackoverflow.com/questions/58142680/iterating-through-a-range-in-both-directions/58142693#58142693) problem and was wondering whether it was possible to use generator functions to solve – Shnick Sep 28 '19 at 02:31
  • And if you try hacking the prototype to create *different* behavior (thus changing the generally accepted semantics of a "Generator") ... at the very least you'd be creating a maintenance nightmare. Just say No :) If you don't like the semantics of "Generator" ... then find (or create) something else. Something unambiguously "different". – paulsm4 Sep 28 '19 at 02:32
  • 2
    @Shnick use a suitable data strucutre instead of using generators which isn't designed for such cases, you can simply use a circular linked list and hold the reference of last used value and go in whatever direction you want, or as answered there by `certainperformace` use the array and modulus technique, ***it's good to see you're trying to solve one problem in many ways i appreciate that, even i do the same all the time, but generator isn't a good choice for this case IMO*** – Code Maniac Sep 28 '19 at 02:35
  • 1
    @CodeManiac yeah, I agree that generators are not good for this case now that I'm aware of their limitations and strengths – Shnick Sep 28 '19 at 02:43

1 Answers1

3

You can have the generator check the result of the yield, and if it's a certain expression (like 'prev'), decrement i instead of incrementing it:

const array = ['a', 'b', 'c'];

function* f(array) {
  let i = 0;
  while (true) {
    const incOrDec = (yield array[i]) === 'prev' ? -1 : 1;
    i = (array.length + i + incOrDec) % array.length;
  }
}

const seq = f(array);
console.log(seq.next().value); // a
console.log(seq.next().value); // b
console.log(seq.next().value); // c

console.log(seq.next().value); // a

console.log(seq.next('prev').value); // c <-- expect 'c'

If you don't build the functionality into the generator, you'll have to explicitly save the result of the previous iteration in a variable and then check it. There's no built-in method like .prev.

I'm inexperienced with generators, and if there is a better data-structure/method to progressively get values (previous and next) I'm open to hearing about those suggestions

If you don't have to work with a generator, and your purpose is to be able to iterate forwards and backwards, then don't. If you start out with a generator (like with an external function you can't control), then you can use a method similar to the one above to transform the generator into one which can navigate forwards and backwards. Otherwise, just use a plain array that you put values into and look up indicies on, eg:

const array = ['a', 'b', 'c'];

function* f(array) {
  let i = 0;
  while (true) {
    const incOrDec = (yield array[i]) === 'prev' ? -1 : 1;
    i = (array.length + i + incOrDec) % array.length;
  }
}

const cache = [];
const getValue = i => cache[i] || (cache[i] = /* put logic for retrieving value here */ array[i % array.length]);

console.log(getValue(0));
console.log(getValue(1));
console.log(getValue(2));
console.log(getValue(3));
console.log(getValue(4));
console.log(getValue(3));
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • 3
    Like Code Maniac asked: what is the use case for this? The simple answer to the question is "There's no built-in method like .prev". Period. – paulsm4 Sep 28 '19 at 02:29
  • 1
    Sure, but just saying that isn't as useful as demonstrating a way to achieve the desired functionality using a different method. If you're wondering about the use case, that's a question for the OP – CertainPerformance Sep 28 '19 at 02:35
  • Thank you for the answer, while I'm aware that generators are for progressively "moving forward", this does answer my question and gives me what I'm after – Shnick Sep 28 '19 at 02:35
  • 2
    @CertainPerformance fair enough, but can we add a note why isn't a suitable thing for such case ( as it isn't designed for such cases AFAIK ) and why we should use the suitable thing ( and much more simpler way to achieve the same functionality ) so the future reader will know better ? – Code Maniac Sep 28 '19 at 02:45