Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

When to set "done" to true in an iterator in javascript (off by one errors)

I’ve seen a few iterators implemented in javascript, and every time it seems like they’re off by one (there’s obviously something I’m missing) Here’s an example

const numbers = [1, 2, 3];

numbers[Symbol.iterator] = function() {
  let idx = 0;
  return {
    next: function() {
      return {
        value: numbers[idx++],
        done: idx > numbers.length,
      };
    },
  };
};

// Run the custom iterator:
for (const num of numbers) console.log(num);

In this example, done is set to true when idx > numbers.length. However doesn’t this mean that numbers[3] will not be set as done = true (even though there’s no value for numbers[3]? I don’t fully understand why this code works.

Would appreciate any help — thanks!

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

There are two things to realise:

  1. done does not indicate whether the returned value is the last one, but whether the iterator has no more value to return. As Mozilla contributors write:

    done

    A boolean that’s false if the iterator was able to produce the next value in the sequence.

  2. idx++ evaluates and then increments, and so the expression for the done property works with an idx value that is one more than was used for setting the value property.

So we have these objects returned:

{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }

Alternative code without ++ side-effect

To eliminate the side effect of ++ mentioned in point 2, it maybe helps to look at this version, which produces the exact same {value,done} objects:

const numbers = [1, 2, 3];

numbers[Symbol.iterator] = function() {
  let idx = -1; // <--- one less, so we can first increment
  return {
    next: function() {
      idx++; // First increment
      return {
        value: numbers[idx], // then use
        done: idx >= numbers.length, // ...as expected
      };
    },
  };
};

// Run the custom iterator:
for (const num of numbers) console.log(num);
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading