 # Array.range() for generating sequences

It is reasonably common to need an array populated by a simple sequence of numbers, usually to manipulate them later. Uses include:

• Generating page numbers (`<a href="page1.html">1</a>`)
• Retries (n attempts to login before being locked out)
• Mathematics (any ranged sum or product, combinatorics, Taylor/Maclaurin series, partial harmonic series, Fourier series...)

Currently these can be created imperatively via `for` loop:

``````function range(start, stop, step){
let arr = [];
for(let i = 0; i < start+stop; i++){
arr[i] = start + (i * step);
}
return arr;
}
``````

(Of course, one could elect to make any manipulations they wanted inside the `for` loop, rather than generating the array and then manipulating it.)

One can also create such an array more functionally using array methods in various ways:

``````const range = (start, stop, step) => Array((stop - start) / step)
.fill(0)
.map((_,i) => start + (i * step));
// or
const range = (start, stop, step) => Array(stop - start)
.fill(0)
.map((_,i) => start + i)
.filter(v => (v-start) % step === 0)
``````

The `for` loop solution has detractors simply due to its use of `for` loops, but in any case it uses a fair amount of code for a fairly simple concept.

The same problem applies to the `Array` method approach -- an array created via `Array(n)` cannot be meaningfully mapped over until it is filled with something either via `for` loop or with `Array.prototype.fill()`, which can only fill with a constant value. Meaning a third step (at least) is required to generate the final array, and at least two passes over the entire array.

Additionally, the `Array` methods approach has a lot of potential pitfalls (there are issues with both of my demo implementations, edge cases where they don't work, etc).

I think it would be useful to have a static method on `Array`, tentatively called `Array.range`, with the following semantics:

• `Array.range(stop: number)` -- for any positive `stop` return an array of integers in the interval `[0, Math.ceil(stop) - 1]` inclusive, ordered from least to greatest. For any negative `stop` return an array of integers in the interval `[Math.floor(stop) + 1, 0]` inclusive, tentatively ordered from greatest to least (though I'd be open to arguments either way on that). For `stop === 0`, return `[]`, or possibly ``, it's up for discussion.
• `Array.range(start: number, stop: number, step = 1)` -- If `start` is less than `stop`, return an array of the set of all `n_k = start + k*Math.abs(step)` where `k` is a non-negative integer and `n_k` is in the interval `[start, stop)` (note that it is inclusive on the low end, exclusive on the high end a la `Array.prototype.slice()`). If `start` is greater than `stop`, returns an array of the set of all `n_k = start - k * Math.abs(step)` where `k` is a non-negative integer and `n_k` is in the interval `[start, stop)` with the same restrictions as the prior case. Throw an error (`RangeError`?) on `step === 0`. Either throw an error, or return `[]` or `[start]` on `start === stop`.
• Possibly? `Array.range(start: number, stop: number, step: number, modifier: (value: number index: number)=> number)` with the same logic as the prior case, but the function `modifier` is mapped across the resultant array in the same pass as creation (reducing the need to then immediately call `.map()` on the array, which would also introduce an additional pass over the array)

I think that having this baked into the `Array` class would allow for some optimizations at the interpreter level that would be more difficult or impossible to do in a library, such as making the creation slightly more lazy in some cases.

Is this (or a version of this) something that anyone else would support?

Hi David,

This seems like a natural extension for https://github.com/tc39/proposal-slice-notation?

There's also a much older proposal, http://array.build, which could be relevant.

Yes! Please! `Array.build` seems wonderful.

``````const arr1 = [1:6]; // [1, 2, 3, 4, 5, 6]
const arr2 = [1:7:2]; // [1, 3, 5, 7]
const arr3 = [1:4, 10:20:5, 4, 2:4]; // [1, 2, 3, 4, 10, 15, 20, 4, 2, 3, 4]

for(const n of [4:8]) { ... }

[10:100:10][0:4]; // [10, 20, 30, 40]
``````

There is another proposal for it https://github.com/Jack-Works/proposal-Number.range

1 Like