skip to Main Content

I’m trying to understand iterables in JavaScript by creating a range function, but it doesn’t work

I’m trying to understand iterables in JavaScript so I created a range function that when I call it, it will return a range of numbers for me e.g:

for (let i of Range(5)) console.log(i);
0
1
2
3
4

here is the code for doing it.

// log function
const log = console.log;

// Range constructor 
function Range(end, start, step = 1) {
    if (!new.target) {
        return new Range(start, end, step);
    }
    end = start ? start : end;
    start = start ? end : 0;
    step = step;
    
    return {
        start,
        end,
        step,
        [Symbol.iterator]() {
            return this;
        },
        next() {
            if (this.start < this.end) {
                this.start += this.step;
                return {
                    done: false,
                    value: this.start - this.step
                };
            } else {
                return { done: true };
            }
        }
    }
}

but when I run it, return the numbers one after another e.g.

let range = new Range(6);
for (i of range) log(i);

Output

0
1
2
3
4
5

but when I run it like for (let i of Range(5) log(i); it return undefined Why!

update if fixed the problem by updating return new Range(start, end, step); to return new Range(end, start, step); and end = start ? start : end; to end = start ?? end;

so when I run

let range = new Range(5); 
for (let i of range) log( i ); // 0, 1, 2, 3, 4
for (let i of Range(10) ) log( i ); // 0, 1, 2, 3, 4

but a new problem raised

let arr = Array.from(range); 
log(arr) // []

how to fix it

2

Answers


  1. You have mixed up your arguments in new Range(start, end, step) (thanks @Unmitigated). It should be new Range(end, start, step).


    FYI a generator function is far simpler.

    function* range(start, end, step = 1) {
      if (end === undefined) {
        end = start;
        start = 0;
      }
      for (; start < end; start += step) {
        yield start;
      }
    }
    
    Login or Signup to reply.
  2. How about something like this ?

     let range = {
       from: 1,
       to: 5
     };
    
     // 1. call to for..of initially calls this. 
     range[Symbol.iterator] = function() {
    
      return {
          current: this.from,
          last: this.to,
    
          // 3. next() is called on each iteration by the for..of loop
         next() {
            if (this.current <= this.last) {
                return {
                    done: false,
                    value: this.current++
                };
            } else {
                return {
                    done: true
                };
            }
         }
      };
    };
    
    for (let num of range) {
     console.log(num); // 1, then 2, 3, 4, 5
    }
    

    UPDATE

    If we need to consider stepSize we can define a function as follows:

    function myInterator(start = 0, end = 5, step = 1) {
     let nextIndex = start;
     let iterationCount = 0;
    
     const rangeIterator = {
       next() {
         let result;
         if (nextIndex < end) {
           result = { value: nextIndex, done: false };
           nextIndex += step;
           iterationCount++;
           return result;
          }
        return { value: iterationCount, done: true };
       },
    };
     return rangeIterator;
    }
    

    Now, use the function as follows:

    const it = myIterator(1, 5, 1);
    
     let result = it.next();
     while (!result.done) {
      console.log(result.value); // 1 2 3 4 5
      result = it.next();
     }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search