[javascript] How to generate range of numbers from 0 to n in ES2015 only?

I have always found the range function missing from JavaScript as it is available in python and others? Is there any concise way to generate range of numbers in ES2015 ?

EDIT: MY question is different from the mentioned duplicate as it is specific to ES2015 and not ECMASCRIPT-5. Also I need the range to be starting from 0 and not specific starting number (though it would be good if that is there)

This question is related to javascript arrays ecmascript-6

The answer is


You can use the spread operator on the keys of a freshly created array.

[...Array(n).keys()]

or

Array.from(Array(n).keys())

The Array.from() syntax is necessary if working with TypeScript


For numbers 0 to 5

[...Array(5).keys()];
=> [0, 1, 2, 3, 4]

You can use a generator function, which creates the range lazily only when needed:

_x000D_
_x000D_
function* range(x, y) {_x000D_
  while (true) {_x000D_
    if (x <= y)_x000D_
      yield x++;_x000D_
_x000D_
    else_x000D_
      return null;_x000D_
  }_x000D_
}_x000D_
_x000D_
const infiniteRange = x =>_x000D_
  range(x, Infinity);_x000D_
  _x000D_
console.log(_x000D_
  Array.from(range(1, 10)) // [1,2,3,4,5,6,7,8,9,10]_x000D_
);_x000D_
_x000D_
console.log(_x000D_
  infiniteRange(1000000).next()_x000D_
);
_x000D_
_x000D_
_x000D_

You can use a higher order generator function to map over the range generator:

_x000D_
_x000D_
function* range(x, y) {_x000D_
  while (true) {_x000D_
    if (x <= y)_x000D_
      yield x++;_x000D_
_x000D_
    else_x000D_
      return null;_x000D_
  }_x000D_
}_x000D_
_x000D_
const genMap = f => gx => function* (...args) {_x000D_
  for (const x of gx(...args))_x000D_
    yield f(x);_x000D_
};_x000D_
_x000D_
const dbl = n => n * 2;_x000D_
_x000D_
console.log(_x000D_
  Array.from(_x000D_
    genMap(dbl) (range) (1, 10)) // [2,4,6,8,10,12,14,16,18,20]_x000D_
);
_x000D_
_x000D_
_x000D_

If you are fearless you can even generalize the generator approach to address a much wider range (pun intended):

_x000D_
_x000D_
const rangeBy = (p, f) => function* rangeBy(x) {_x000D_
  while (true) {_x000D_
    if (p(x)) {_x000D_
      yield x;_x000D_
      x = f(x);_x000D_
    }_x000D_
_x000D_
    else_x000D_
      return null;_x000D_
  }_x000D_
};_x000D_
_x000D_
const lte = y => x => x <= y;_x000D_
_x000D_
const inc = n => n + 1;_x000D_
_x000D_
const dbl = n => n * 2;_x000D_
_x000D_
console.log(_x000D_
  Array.from(rangeBy(lte(10), inc) (1)) // [1,2,3,4,5,6,7,8,9,10]_x000D_
);_x000D_
_x000D_
console.log(_x000D_
  Array.from(rangeBy(lte(256), dbl) (2)) // [2,4,8,16,32,64,128,256]_x000D_
);
_x000D_
_x000D_
_x000D_

Keep in mind that generators/iterators are inherently stateful that is, there is an implicit state change with each invocation of next. State is a mixed blessing.


How about just mapping ....

Array(n).map((value, index) ....) is 80% of the way there. But for some odd reason it does not work. But there is a workaround.

Array(n).map((v,i) => i) // does not work
Array(n).fill().map((v,i) => i) // does dork

For a range

Array(end-start+1).fill().map((v,i) => i + start) // gives you a range

Odd, these two iterators return the same result: Array(end-start+1).entries() and Array(end-start+1).fill().entries()


Here's another variation that doesn't use Array.

let range = (n, l=[], delta=1) => {
  if (n < 0) { 
    return l 
  }
  else {
    l.unshift(n)
    return range(n - delta, l) 
  }
}

I also found one more intuitive way using Array.from:

const range = n => Array.from({length: n}, (value, key) => key)

Now this range function will return all the numbers starting from 0 to n-1

A modified version of the range to support start and end is:

const range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);

EDIT As suggested by @marco6, you can put this as a static method if it suits your use case

Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);

and use it as

Array.range(3, 9)

A lot of these solutions build on instantiating real Array objects, which can get the job done for a lot of cases but can't support cases like range(Infinity). You could use a simple generator to avoid these problems and support infinite sequences:

function* range( start, end, step = 1 ){
  if( end === undefined ) [end, start] = [start, 0];
  for( let n = start; n < end; n += step ) yield n;
}

Examples:

Array.from(range(10));     // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array.from(range(10, 20)); // [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]

i = range(10, Infinity);
i.next(); // { value: 10, done: false }
i.next(); // { value: 11, done: false }
i.next(); // { value: 12, done: false }
i.next(); // { value: 13, done: false }
i.next(); // { value: 14, done: false }

const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);

in Typescript


To support delta

const range = (start, end, delta) => {
  return Array.from(
    {length: (end - start) / delta}, (v, k) => (k * delta) + start
  )
};

You can also do it with a one liner with step support like this one:

((from, to, step) => ((add, arr, v) => add(arr, v, add))((arr, v, add) => v < to ? add(arr.concat([v]), v + step, add) : arr, [], from))(0, 10, 1)

The result is [0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9].


With Delta/Step

smallest and one-liner
[...Array(N)].map((_, i) => from + i * step);

Examples and other alternatives

[...Array(10)].map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

Array.from(Array(10)).map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

Array.from(Array(10).keys()).map(i => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

[...Array(10).keys()].map(i => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]

Array(10).fill(0).map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

Array(10).fill().map((_, i) => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]
Range Function
const range = (from, to, step) =>
  [...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);

range(0, 9, 2);
//=> [0, 2, 4, 6, 8]

// can also assign range function as static method in Array class (but not recommended )
Array.range = (from, to, step) =>
  [...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);

Array.range(2, 10, 2);
//=> [2, 4, 6, 8, 10]

Array.range(0, 10, 1);
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Array.range(2, 10, -1);
//=> []

Array.range(3, 0, -1);
//=> [3, 2, 1, 0]
As Iterators
class Range {
  constructor(total = 0, step = 1, from = 0) {
    this[Symbol.iterator] = function* () {
      for (let i = 0; i < total; yield from + i++ * step) {}
    };
  }
}

[...new Range(5)]; // Five Elements
//=> [0, 1, 2, 3, 4]
[...new Range(5, 2)]; // Five Elements With Step 2
//=> [0, 2, 4, 6, 8]
[...new Range(5, -2, 10)]; // Five Elements With Step -2 From 10
//=>[10, 8, 6, 4, 2]
[...new Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]

// Also works with for..of loop
for (i of new Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2
As Generators Only
const Range = function* (total = 0, step = 1, from = 0) {
  for (let i = 0; i < total; yield from + i++ * step) {}
};

Array.from(Range(5, -2, -10));
//=> [-10, -12, -14, -16, -18]

[...Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]

// Also works with for..of loop
for (i of Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2

// Lazy loaded way
const number0toInf = Range(Infinity);
number0toInf.next().value;
//=> 0
number0toInf.next().value;
//=> 1
// ...

From-To with steps/delta

using iterators
class Range2 {
  constructor(to = 0, step = 1, from = 0) {
    this[Symbol.iterator] = function* () {
      let i = 0,
        length = Math.floor((to - from) / step) + 1;
      while (i < length) yield from + i++ * step;
    };
  }
}
[...new Range2(5)]; // First 5 Whole Numbers
//=> [0, 1, 2, 3, 4, 5]

[...new Range2(5, 2)]; // From 0 to 5 with step 2
//=> [0, 2, 4]

[...new Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]
using Generators
const Range2 = function* (to = 0, step = 1, from = 0) {
  let i = 0,
    length = Math.floor((to - from) / step) + 1;
  while (i < length) yield from + i++ * step;
};

[...Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]

let even4to10 = Range2(10, 2, 4);
even4to10.next().value;
//=> 4
even4to10.next().value;
//=> 6
even4to10.next().value;
//=> 8
even4to10.next().value;
//=> 10
even4to10.next().value;
//=> undefined

For Typescript

interface _Iterable extends Iterable<{}> {
  length: number;
}

class _Array<T> extends Array<T> {
  static range(from: number, to: number, step: number): number[] {
    return Array.from(
      <_Iterable>{ length: Math.floor((to - from) / step) + 1 },
      (v, k) => from + k * step
    );
  }
}
_Array.range(0, 9, 1);
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

Update

class _Array<T> extends Array<T> {
  static range(from: number, to: number, step: number): number[] {
    return [...Array(Math.floor((to - from) / step) + 1)].map(
      (v, k) => from + k * step
    );
  }
}
_Array.range(0, 9, 1);

Edit

class _Array<T> extends Array<T> {
  static range(from: number, to: number, step: number): number[] {
    return Array.from(Array(Math.floor((to - from) / step) + 1)).map(
      (v, k) => from + k * step
    );
  }
}
_Array.range(0, 9, 1);

Range with step ES6, that works similar to python list(range(start, stop[, step])):

const range = (start, stop, step = 1) => {
  return [...Array(stop - start).keys()]
    .filter(i => !(i % Math.round(step)))
    .map(v => start + v)
}

Examples:

range(0, 8) // [0, 1, 2, 3, 4, 5, 6, 7]
range(4, 9) // [4, 5, 6, 7, 8]
range(4, 9, 2) // [4, 6, 8] 
range(4, 9, 3) // [4, 7]

Generators now allow you to generate the number sequence lazily and using less memory for large ranges.

While the question specifically states ES2015, I expect a lot of Typescript users will end up here and the conversion to ES is straightforward...

function range(end: number): IterableIterator<number>;
// tslint:disable-next-line:unified-signatures
function range(begin: number, end: number): IterableIterator<number>;

function *range(begin: number, end: number = NaN): IterableIterator<number> {
    let num = 0;
    if (isNaN(end)) {
        end = begin;
    } else {
        num = begin;
    }
    while (num < end) {
        yield num++;
    }
}

The first two function declarations are just to provide more informative completion suggestions in your IDE.


So, in this case, it would be nice if Number object would behave like an Array object with the spread operator.

For instance Array object used with the spread operator:

let foo = [0,1,2,3];
console.log(...foo) // returns 0 1 2 3

It works like this because Array object has a built-in iterator.
In our case, we need a Number object to have a similar functionality:

[...3] //should return [0,1,2,3]

To do that we can simply create Number iterator for that purpose.

Number.prototype[Symbol.iterator] = function *() {
   for(let i = 0; i <= this; i++)
       yield i;
}

Now it is possible to create ranges from 0 to N with the spread operator.

[...N] // now returns 0 ... N array

http://jsfiddle.net/01e4xdv5/4/

Cheers.


This function will return an integer sequence.

const integerRange = (start, end, n = start, arr = []) =>
  (n === end) ? [...arr, n]
    : integerRange(start, end, start < end ? n + 1 : n - 1, [...arr, n]);

$> integerRange(1, 1)
<- Array [ 1 ]

$> integerRange(1, 3)
<- Array(3) [ 1, 2, 3 ]

$> integerRange(3, -3)
<- Array(7) [ 3, 2, 1, 0, -1, -2, -3 ]

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to arrays

PHP array value passes to next row Use NSInteger as array index How do I show a message in the foreach loop? Objects are not valid as a React child. If you meant to render a collection of children, use an array instead Iterating over arrays in Python 3 Best way to "push" into C# array Sort Array of object by object field in Angular 6 Checking for duplicate strings in JavaScript array what does numpy ndarray shape do? How to round a numpy array?

Examples related to ecmascript-6

"Uncaught SyntaxError: Cannot use import statement outside a module" when importing ECMAScript 6 where is create-react-app webpack config and files? Can (a== 1 && a ==2 && a==3) ever evaluate to true? How do I fix "Expected to return a value at the end of arrow function" warning? Enums in Javascript with ES6 Home does not contain an export named Home How to scroll to an element? How to update nested state properties in React eslint: error Parsing error: The keyword 'const' is reserved Node.js ES6 classes with require