Posted on • Edited on • Originally published atantoniovdlc.me
Implementing ranges in JavaScript
Ranges are natively supported by a few (popular) programming languages. They allow for iteration over a defined space, while not have a linear increase in their memory footprint (all ranges always store a similar amount of data).
Let's try adding a similar idiom to JavaScript!
One way to approach this challenge is to write a plugin for a transpiler (for example a babel plugin) that would allow the following syntax:
constrange=(0..5)for(letiofrange){console.log(i)// 0, 1, 2, 3, 4}
Instead, we will provide a similar functionality with vanilla JavaScript.
for(letiofrange(0,5)){console.log(i)// 0, 1, 2, 3, 4}
We does the range stop at
4
instead of5
? This is a design choice, and as most languages that have a built-inrange
as not inclusive of their last value, therange
utility that we will build will follow a similar convention by default.
The above syntax also allows us to pass a third argument to the function to control thestep
in between each iteration:
for(letiofrange(0,10,2)){console.log(i)// 0, 2, 4, 6, 8}
To start, let's create a classRange
which will host the data needed for a range:
classRange{constructor(start,stop,step=1){this._start=Number(start);this._stop=Number(stop);this._step=Number(step);// Initialise a counter for iterationthis.i=Number(start);}first(){returnthis._start;}last(){returnthis._stop;}step(){returnthis._step;}}
We can now create a very basic (and not very useful) range:
constrange=newRange(0,10);range.first();// 0range.last();// 10range.step();// 1 (by default)
One of the main reasons we want ranges though is to iterate over them ... so let's implementiteration protocols in ourRange
class!
To do so, we need to implement anext()
method, as well as a[Symbol.iterator]
method.
classRange{constructor(start,stop,step=1){...// Initialise a counter for iterationthis.i=Number(start);}first(){...}last(){...}step(){...}next(){if(this.i<this._stop){constvalue=this.i;this.i+=this._step;return{value,done:false};}return{value:undefined,done:true};}[Symbol.iterator](){returnthis;}}
Great! Now we can use our ranges as follow:
constrange=newRange(0,5)for(letiofrange){console.log(i)// 0, 1, 2, 3, 4}
or
constrange=newRange(0,5)range.next()// { value: 0, done: false }range.next()// { value: 1, done: false }range.next()// { value: 2, done: false }range.next()// { value: 3, done: false }range.next()// { value: 4, done: false }range.next()// { value: undefined, done: true }
There is one issue with our current implementation though, and that is that the range is depleted after a single iteration. We cannot reuse the same range in multiple consecutive loops.
Luckily, there is a one line fix to support that:
classRange{constructor(start,stop,step=1){...// Initialise a counter for iterationthis.i=Number(start);}first(){...}last(){...}step(){...}next(){if(this.i<this._stop){constvalue=this.i;this.i+=this._step;return{value,done:false};}// We reset the value once we have iterated over all values so that// ranges are reusable.this.i=this._start;return{value:undefined,done:true};}[Symbol.iterator](){returnthis;}}
Finally, to achieve the semantics we defined at the beginning, we need to wrap our class creation in a function:
classRange{...}functionrange(start,stop,step=1){returnnewRange(start,stop,step);}for(letiofrange(0,5)){console.log(i)// 0, 1, 2, 3, 4}
Again, inspired by this blog post, I decided to build a library with the aforementioned features and much more! Check it out:
AntonioVdlC / range
⛰ - Implement ranges in JavaScript
range
Implement ranges in JavaScript.
Installation
This package is distributed via npm:
npm install @antoniovdlc/range
Motivation
Ranges are natively supported by a few (popular) programming languages. They allow for iteration over a defined space, while not having a linear increase in their memory footprint (all ranges always store a similar amount of data).
Usage
You can use this library either as an ES module or a CommonJS package:
importrangefrom"@antoniovdlc/range";
- or -
constrange=require("@antoniovdlc/range");
To create a range:
conststart=0;conststop=10;conststep=2;// Defaults to `1` if not passedconstinclusive=true;// Defaults to `false` if not passedconstr=range(start,stop,step,inclusive);
You can also pass an options object for convenience:
conststart=0;…
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse