142

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)

Aditya Singh
  • 13,438
  • 12
  • 35
  • 62
  • The answer is the same for ES5 and ES6. – loganfsmyth Apr 29 '16 at 22:16
  • 1
    But you can always use some og the new concepts such as generators, new array methods etc. in ES2015. That gives you extra set of tools to achieve the task – Aditya Singh Apr 29 '16 at 22:17
  • 7
    I think @Delapouite has the perfect answer to this in [comments to an answer to the duplicated question](http://stackoverflow.com/a/29559488/918910): `[...Array(n).keys()]`. – jib Apr 29 '16 at 23:14
  • related: [Is there a mechanism to loop x times in ES6 without mutable variables?](https://stackoverflow.com/q/30452263/1048572) and [functional way to iterate over range in ES6](https://stackoverflow.com/q/30650961/1048572) – Bergi Aug 11 '17 at 11:28
  • 2
    `[...Array(5)].map((_,i) => i+1)` –  Feb 26 '19 at 16:36

15 Answers15

280

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

Jeremy Moritz
  • 10,834
  • 6
  • 30
  • 37
Delapouite
  • 7,410
  • 5
  • 28
  • 37
  • 45
    Sweet: `function range (start, end) { return [...Array(1+end-start).keys()].map(v => start+v) }` – conny Nov 02 '16 at 10:34
  • 2
    This doesn't work in typescript because keys() returns an Array Iterator instead of an Array. Checkout aditya-singh's answer for a more universal approach. – David Domingo Jul 25 '17 at 10:44
  • 7
    …… or **`Array.from(Array(n).keys())`**. – Константин Ван Feb 04 '18 at 22:59
  • 3
    @DavidGonzalezShannon Do you know why `[...Array(n).keys()]` doesn't work in Typescript? Is it an intentional deviation from other JS implementations? – Stu Cox Jul 19 '18 at 07:47
  • Hey @StuCox I have no idea why but it transpiles it to `Array(5).keys().slice()` and slice is not a method of array iterator. Here's an example of it not working https://www.typescriptlang.org/play/index.html#src=const%20range%20%3D%20%5B...Array(5).keys()%5D%3B%0D%0Aconsole.log(range)%3B – David Domingo Jul 20 '18 at 18:02
  • @DavidDomingo add the downlevelIteration flag or change your target to es6 or later. – Chris_F Dec 19 '18 at 12:50
  • Perf test here: https://jsbench.me/qvk74npuvm/1 Dr Axel's thoughts on array perf here: https://2ality.com/2018/12/creating-arrays.html#tip%3A-array-performance-usually-doesn%E2%80%99t-matter-that-much :P – Sebastian Thomas Feb 27 '20 at 11:21
105

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)
Aditya Singh
  • 13,438
  • 12
  • 35
  • 62
  • 1
    Nice one! Why don't we extend Array static interface with it? In typescript works great with: `interface ArrayConstructor { range(n: number): number[]; }` `Array.range = n => Array.from({length: n}, (value, key) => key);` And then everywhere `Array.range(x)...` – marco6 Sep 03 '17 at 21:48
  • `[ts] Property 'range' does not exist on type 'ArrayConstructor'`. thouths? – angularrocks.com Mar 08 '18 at 04:42
  • 2
    Overriding built-ins is considered bad practice in javascript now. – jhohlfeld Feb 21 '19 at 19:47
29

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);
nkitku
  • 1,507
  • 15
  • 14
  • Your updated TypeScript version doesn't work. It creates an empty array with the indicated size. You need to use Array.from with Array.keys with TypeScript. `Array.from(Array(~~((to - from) / step) + 1).keys())` – David Domingo Oct 24 '18 at 14:41
20

For numbers 0 to 5

[...Array(5).keys()];
=> [0, 1, 2, 3, 4]
Ben
  • 20,171
  • 2
  • 18
  • 25
13

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 }
Iron Savior
  • 3,940
  • 3
  • 21
  • 30
10

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.

Getriax
  • 441
  • 1
  • 7
  • 11
4

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

function* range(x, y) {
  while (true) {
    if (x <= y)
      yield x++;

    else
      return null;
  }
}

const infiniteRange = x =>
  range(x, Infinity);
  
console.log(
  Array.from(range(1, 10)) // [1,2,3,4,5,6,7,8,9,10]
);

console.log(
  infiniteRange(1000000).next()
);

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

function* range(x, y) {
  while (true) {
    if (x <= y)
      yield x++;

    else
      return null;
  }
}

const genMap = f => gx => function* (...args) {
  for (const x of gx(...args))
    yield f(x);
};

const dbl = n => n * 2;

console.log(
  Array.from(
    genMap(dbl) (range) (1, 10)) // [2,4,6,8,10,12,14,16,18,20]
);

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

const rangeBy = (p, f) => function* rangeBy(x) {
  while (true) {
    if (p(x)) {
      yield x;
      x = f(x);
    }

    else
      return null;
  }
};

const lte = y => x => x <= y;

const inc = n => n + 1;

const dbl = n => n * 2;

console.log(
  Array.from(rangeBy(lte(10), inc) (1)) // [1,2,3,4,5,6,7,8,9,10]
);

console.log(
  Array.from(rangeBy(lte(256), dbl) (2)) // [2,4,8,16,32,64,128,256]
);

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.

4

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]
juanesarango
  • 396
  • 5
  • 10
  • 1
    Nice add to the question! This helped me out to get much cleaner code in my Angular 8 html *ngFor loop templates. – Sam Dec 14 '19 at 08:47
2

To support delta

const range = (start, end, delta) => {
  return Array.from(
    {length: (end - start) / delta}, (v, k) => (k * delta) + start
  )
};
fubar
  • 14,318
  • 3
  • 29
  • 34
2

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()

Arturo Hernandez
  • 2,274
  • 2
  • 24
  • 31
1

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].

Marcin Król
  • 1,314
  • 2
  • 13
  • 27
1

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 ]
Pilosa
  • 433
  • 1
  • 3
  • 12
Zack
  • 196
  • 9
0
const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);

in Typescript

PeiSong
  • 581
  • 1
  • 4
  • 11
  • There's no reason to use both `Array.from` and spread syntax. And then it's exactly the same as the existing answer. – Bergi Aug 15 '17 at 07:40
  • Just wanna point out `[...Array(n).keys()]` does not work in Typescript. – PeiSong Aug 15 '17 at 08:52
  • 3
    Then use `Array.from(Array(n).keys())`. I'm pretty sure it should work though, what does the literal with spread syntax transpile to? – Bergi Aug 15 '17 at 15:29
0

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) 
  }
}
Han Lazarus
  • 1,503
  • 2
  • 9
  • 11
0

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.

Dave
  • 607
  • 5
  • 16