JavaScript function similar to Python range()

Question:

Is there a function in JavaScript similar to Python’s range()?

I think there should be a better way than to write the following lines every time:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}
Asked By: clwen

||

Answers:

No, there is none, but you can make one.

JavaScript’s implementation of Python’s range()

Trying to emulate how it works in Python, I would create function similar to this:

function range(start, stop, step) {
    if (typeof stop == 'undefined') {
        // one param defined
        stop = start;
        start = 0;
    }

    if (typeof step == 'undefined') {
        step = 1;
    }

    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
        return [];
    }

    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
        result.push(i);
    }

    return result;
};

See this jsfiddle for a proof.

Comparison between range() in JavaScript and Python

It works in the following way:

  • range(4) returns [0, 1, 2, 3],
  • range(3,6) returns [3, 4, 5],
  • range(0,10,2) returns [0, 2, 4, 6, 8],
  • range(10,0,-1) returns [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
  • range(8,2,-2) returns [8, 6, 4],
  • range(8,2) returns [],
  • range(8,2,2) returns [],
  • range(1,5,-1) returns [],
  • range(1,5,-2) returns [],

and its Python counterpart works exactly the same way (at least in the mentioned cases):

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

So if you need a function to work similarly to Python’s range(), you can use above mentioned solution.

Answered By: Tadeck

Here you go.

This will write (or overwrite) the value of each index with the index number.

Array.prototype.writeIndices = function( n ) {
    for( var i = 0; i < (n || this.length); ++i ) this[i] = i;
    return this;
};

If you don’t provide a number, it will use the current length of the Array.

Use it like this:

var array = [].writeIndices(10);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Answered By: RightSaidFred

2018: this answer keeps getting upvotes, so here’s an update. The code below is obsolete, but luckily ES6 standardized generators and the yield keyword, and they are universally supported across platforms. An example of the lazy range() using yield can be found here.


In addition to what’s already said, Javascript 1.7+ provides support for iterators and generators which can be used to create a lazy, memory-efficient version of range, simlar to xrange in Python2:

function range(low, high) {  
    return {
        __iterator__: function() {
            return {  
                next: function() {
                    if (low > high)
                        throw StopIteration;  
                    return low++;
                }
            }
        }
    }
}

for (var i in range(3, 5))  
  console.log(i); // 3,4,5
Answered By: georg

You may use underscore library. It contains dozens of useful functions for working with arrays and many more.

Answered By: Radagast

A port of the range function from Python 2 is provided by the underscore.js and lodash utility libraries (along with many other useful tools). Examples copied from the underscore docs:

_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []
Answered By: Mark Amery

Fusing together both answers from @Tadeck and @georg, I came up with this:

function* range(start, stop, step = 1) {
    if (stop == null) {
        // one param defined
        stop = start;
        start = 0;
    }

    for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
        yield i;
    }
}

To use it in a for loop you need the ES6/JS1.7 for-of loop:

for (let i of range(5)) {
    console.log(i);
}
// Outputs => 0 1 2 3 4

for (let i of range(0, 10, 2)) {
    console.log(i);
}
// Outputs => 0 2 4 6 8

for (let i of range(10, 0, -2)) {
    console.log(i);
}
// Outputs => 10 8 6 4 2
Answered By: janka102

Further refined with ES6 default parameters.

let range = function*(start = 0, stop, step = 1) {
  let cur = (stop === undefined) ? 0 : start;
  let max = (stop === undefined) ? start : stop;
  for (let i = cur; step < 0 ? i > max : i < max; i += step)
    yield i
}
Answered By: Volv

For getting an array of size x, here’s an one-liner without using any library

var range = n => Array(n + 1).join(1).split('').map((x, i) => i)

works as

> range(4)
[0, 1, 2, 3]
Answered By: bigOmega ツ

For a very simple range in ES6:

let range = n => Array.from(Array(n).keys())

From bigOmega’s comment, this can be shortened using Spread syntax:

let range = n => [...Array(n).keys()]
Answered By: user1969453

Still no built-in function that is equivalent to range(), but with the most recent version – ES2015 – you can build your own implementation. Here’s a limited version of it. Limited because it doesn’t take into account the step parameter. Just min, max.

const range = (min = null, max = null) =>
  Array.from({length:max ? max - min : min}, (v,k) => max ? k + min : k)

This is accomplished by the Array.from method able to build an array from any object that has a length property. So passing in a simple object with just the length property will create an ArrayIterator that will yield length number of objects.

Answered By: Steve Brownlee

Here’s a small extension for one of the answers in case you need to specify both starting and ending position of the range:

let range = (start, end) => Array.from(Array(end + 1).keys()).slice(start);
Answered By: Dmitrii Mikhailov

The following is a natural adaption of Python’s range() function to JavaScript:

// Generate range from start (inclusive) to stop (exclusive):
function* range(start, stop, step = 1) {
   if (stop === undefined) [start, stop] = [0, start];
   if (step > 0) while (start < stop) yield start, start += step;
   else if (step < 0) while (start > stop) yield start, start += step;
   else throw new RangeError('range() step argument invalid');
} 

// Examples:
console.log([...range(3)]);       // [0, 1, 2]
console.log([...range(0, 3)]);    // [0, 1, 2]
console.log([...range(0, 3, -1)]);// []
console.log([...range(0, 0)]);    // []
console.log([...range(-3)]);      // []
console.log([...range(-3, 0)]);   // [-3, -2, -1]

It supports any argument which can be compared to 0 and stop and can be incremented by step. It behaves identical to the Python version when used with numbers not exceeding Number.MAX_SAFE_INTEGER.

Please note the following corner cases:

[...range(0, 0, 0)];        // RangeError: range() step argument invalid
[...range(Number.MAX_SAFE_INTEGER + 1, Number.MAX_SAFE_INTEGER + 2)];  // []
[...range(Number.MAX_SAFE_INTEGER + 2, Number.MAX_SAFE_INTEGER + 3)];  // Infinite loop
[...range(0.7, 0.8, 0.1)];  // [0.7, 0.7999999999999999]
[...range('1', '11')];      // ['1']
[...range('2', '22')];      // Infinite loop

In contrast to @Tadeck’s, @Volv’s and @janka102’s answer which return [], undefined or enter an infinite loop when step evaluates to 0 or NaN, this generator function throws an exception similar to Python’s behavior.

Answered By: le_m

Can be achieved by attaching an iterator to the Number prototype

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

[...5] // will result in [0,1,2,3,4,5]

Taken from Kyle Simpson’s course Rethinking Asynchronous JavaScript

Answered By: MCH

Here is another es6 implementation of the range

// range :: (from, to, step?) -> [Number]
const range = (from, to, step = 1) => {
  //swap values if necesery
  [from, to] = from > to ? [to, from] : [from, to]
  //create range array
  return [...Array(Math.round((to - from) / step))]
    .map((_, index) => {
      const negative = from < 0 ? Math.abs(from) : 0
      return index < negative ? 
        from + index * step  :
        (index - negative + 1) * step
    })
}  

range(-20, 0, 5)
  .forEach(val => console.log(val))

for(const val of range(5, 1)){
   console.log(`value ${val}`)
}

Answered By: mrFunkyWisdom

pythonic mimics the Python range behaviour best it can using JS’ generators (yield), supporting both the range(stop) and range(start, stop, step) use cases. In addition, pythonic‘s range function returns an Iterator object similar to Python that supports map and filter, so one could do fancy one-liners like:

import {range} from 'pythonic';
// ...
const results = range(5).map(wouldBeInvokedFiveTimes);
// `results` is now an array containing elements from
// 5 calls to wouldBeInvokedFiveTimes

Install using npm:

npm install --save pythonic

Disclosure I’m author and maintainer of Pythonic

Answered By: Keyvan

MDN recommends this approach: Sequence generator (range)

// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

// Generate numbers range 0..4
console.log("range(0, 4, 1):", range(0, 4, 1));
// [0, 1, 2, 3, 4] 

// Generate numbers range 1..10 with step of 2 
console.log("nrange(1, 10, 2):", range(1, 10, 2));
// [1, 3, 5, 7, 9]

// Generate the alphabet using Array.from making use of it being ordered as a sequence
console.log("nrange('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x))", range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x)));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

Answered By: IliasT

No, there is none, but you can make one.

I’m partial to Python3 behavior of range. You will find below JavaScript’s implementation of Python’s range():

function* range(start=0, end=undefined, step=1) {    
    if(arguments.length === 1) {end = start, start = 0}    
    
    [...arguments].forEach(arg => {    
        if( typeof arg !== 'number') {throw new TypeError("Invalid argument")}                               
    })    
    if(arguments.length === 0) {throw new TypeError("More arguments neede")}    
        
    if(start >= end) return                                                                                                                                     
    yield start    
    yield* range(start + step, end, step)    
}    
         
// Use Cases
console.log([...range(5)])

console.log([...range(2, 5)])

console.log([...range(2, 5, 2)])
console.log([...range(2,3)])
// You can, of course, iterate through the range instance.

Answered By: elayira

Assuming you need a simple range with a single step:

let range = (start, end)=> {
    if(start === end) return [start];
    return [start, ...range(start + 1, end)];
}

else

let range = (start, end, step)=> {
    if(start === end) return [start];
    return [start, ...range(start + step, end)];
}

refer to here for more.

Answered By: N Djel Okoye

Is there a function in JavaScript similar to Python’s range()?

All of the solutions here are referring to Python 2’s range (probably because of the code example you gave). However in Python 3, the range() method returns an iterator. JavaScript also has iterators and they’re more space efficient than generating the whole array and storing it in memory.

So the more accurate representation of Python 3’s range(n) function is Array(n).keys().

For example:

for (let i of Array(n).keys()) {
  console.log(i) // 0, 1, 2, 3, ..., n
}

One more example (which has already been covered in the other answers). Converting the iterator to an array (ES6):

let ary = [...Array(n).keys()];
// ary = [0, 1, 2, 3, ..., n]
Answered By: MattCochrane

Is there a function in JavaScript similar to Python’s range()?

As answered before: no, there’s not. But you can make your own.
I believe this is an interesting approach for ES6. It works very similar to Python 2.7 range(), but it’s much more dynamic.

function range(start, stop, step = 1) 
{
    // This will make the function behave as range(stop)
    if(arguments.length === 1)
    {
        return [...Array(arguments[0]).keys()]
    }

    // Adjusts step to go towards the stop value
    if((start > stop && !(step < 0)) ||
       (start < stop && !(step > 0)))
    {
        step *= -1
    }

    let returnArray = []
    // Checks if i is in the interval between start and stop no matter if stop
    // is lower than start or vice-versa
    for(let i = start; (i-start)*(i-stop) <= 0; i += step)
    {
        returnArray.push(i)
    }
    return returnArray
}

This function can behave in three different ways (just like Python’s range()):

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

These examples:

console.log(range(5))
console.log(range(-2, 2))
console.log(range(2, -2))
console.log(range(10, 20, 2))

Will give you the following output:

[ 0, 1, 2, 3, 4 ]
[ -2, -1, 0, 1, 2 ]
[ 2, 1, 0, -1, -2 ]
[ 10, 12, 14, 16, 18, 20 ]

Note that instead of iterating over the array with the in operator (like python), you have to use of. Thus the i variable assumes the value, and not the index, of the array’s element.

for(let i of range(5))
{
    // do something with i...
}
Answered By: sandmann

This is my preferred way. It allows you to specify one or two inputs like in Python.

function range(start, end) {
  return Array.from(Array(end||start).keys()).slice(!!end*start)
}
Answered By: Rob Kwasowski

Here’s how i do it

let n = 5 
[...Array(n).keys()].map(x=>{console.log(x)})

output

0
1
2
3
4
Answered By: Ricky Sahu

An option for NodeJs is to use a Buffer:

[...Buffer.alloc(5).keys()]
// [ 0, 1, 2, 3, 4 ]

What’s nice is that you can iterate directly on the buffer:

Buffer.alloc(5).forEach((_, index) => console.log(index))
// 0
// 1
// 2
// 3
// 4

You can’t do that with an uninitialized Array:

Array(5).forEach((_, index) => console.log(index))
// undefined

But, who in their right mind uses a Buffer for a purpose like this 😉

Answered By: Otto
function range(start, stop) {
    if (typeof stop == 'undefined') {
        stop = start;
        start = 0;
    }
   
    result = [...Array(stop).keys()].slice(start, stop);
    return result;
}
Answered By: MSA

Actually, in Python range() returns an iterable object and we know that iterators are more memory efficient than arrays (or lists in Python).
So if we want to implement the same concept with exact functionality in JavaScript we can use an iterator object:

class range {

constructor(start, stop, step = 1) {
    //check for invalid input
    if (stop !== undefined && typeof stop !== 'number'
        || typeof start !== 'number'
        || typeof step !== 'number') {
        throw Error('invalid input for range function');
    }

    //check if second argument is provided
    if (stop === undefined) {
        stop = start;
        start = 0;
    }

    //initialize the object properties
    this.start = start;
    this.stop = stop;
    this.step = step;
}

//create the iterator object with Symbol.iterator
[Symbol.iterator]() {
    return {
        current: this.start,
        last: this.stop,
        step: this.step,
        //implement the next() method of the iterator
        next() {
            if (this.step === 0) {
                return { done: true };
            } else if (this.step > 0 ? this.current < this.last : this.current > this.last) {
                let value = this.current;
                this.current += this.step;
                return { done: false, value };
            } else {
                return { done: true };
            }
        }
    };
};
}

and for example we have:

for (const num of new range(1, 10, 2)) {
console.log(num);
}

also we can create an array easily:

let arr = [...new range(10, -5, -1)];

or:

let arr = Array.from(new range(10));
Answered By: Mohammad

Recursion function is the best solution for implementing a something like this.

If you want only to get numbers begin from zero

function range(n) {
  if (n > 0){
    return [...range(n-1), n];
   };
   return [0];
};
console.log("range(5) => ", range(5));

For example,

range(5) = [...range(4), 5]
         = [...range(3), 4, 5]
         = [...range(2), 3, 4, 5]
         = [...range(1), 2, 3, 4, 5]
         = [...range(0), 1, 2, 3, 4, 5] // range(0) = [0]
         = [0, 1, 2, 3, 4, 5] //final answer

This function can also extend as following

function range(start, stop, step=1){
  if( stop > start){
    return [...range(start, stop-step), stop];
  }
  return [start];
}
console.log("range(2, 8, 2) => ", range(2, 8, 2));

But note that, unlike in python you have to provide either two or three arguments.

Answered By: Sanuja Methmal

TypeScript’s implementation of Python’s range()

const range = (start: number, stop?: number, step: number = 1) => {
  if (stop === undefined) {
    [start, stop] = [0, start];
  }
  return Array.from({ length: Math.ceil((stop - start) / step) }, (_, i) => start + step * i);
};

console.log(range(4)); // [0, 1, 2, 3]
console.log(range(3, 6)); // [3, 4, 5]
console.log(range(0, 10, 2)); // [0, 2, 4, 6, 8]
console.log(range(10, 0, -1)); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
console.log(range(8, 2, -2)); // [8, 6, 4]
console.log(range(8, 2)); // []
console.log(range(8, 2, 2)); // []
console.log(range(1, 5, -1)); // []
console.log(range(1, 5, -2)); // []
console.log(range(6, 1, -2)); // [6, 4, 2]
console.log(range(1, 6, 2)); // [1, 3, 5]
Answered By: Tyler Liu
/**
 * range generator
 *
 * @param {Integer} start - Optional. An integer specifying at which position to start. Default is 0
 * @param {Integer} stop  - Required. An integer specifying at which position to stop. Excluded.
 * @param {Integer} step  - Optional. An integer specifying the incrementation. Default is 1
 */

function* range (start, stop, step = 1) {

  if (arguments.length === 1) { [start, stop] = [0, start]; }

  if (![start, stop, step].every(Number.isInteger)) { throw new TypeError('range needs integer arguments'); }

  if ((start - stop) * step >= 0) { return []; }

  let check = start > stop ? (a, b) => a > b : (a, b) => a < b;
  while (check(start, stop)) { yield start, start += step; }
}

console.log([...range(4)]);
console.log([...range(2, 4)]);
console.log([...range(1, 4, 2)]);
console.log([...range(-4, -1, 1)]);
console.log([...range(10, 4, -2)]);
console.log([...range(-1, -4, -1)]);
Answered By: mh-firouzjah

A simple approach in Typescript without error checking. Up and down incl. steprate.

const range = (start: number, end: number | null = null, step: number = 1): number[] =>
  [...Array(end === null ? start : Math.abs(end - start)).keys()]
    .filter((n: number): boolean => n % step === 0)
    .map((n: number): number => (end === null ? n : end < start ? Math.max(start, end) - n : n + start));

Same in Javascript ES6

const range = (start, end = null, step = 1) =>
  [...Array(end === null ? start : Math.abs(end - start)).keys()]
    .filter((n) => n % step === 0)
    .map((n) => (end === null ? n : end < start ? Math.max(start, end) - n : n + start));
Answered By: Denis Giffeler
const range = function*(start, stop, inclusive=false) {
    let dx = Math.sign(stop - start);
    if (inclusive) stop += dx;
    for (let x = start; x !== stop; x += dx) yield x;
}

const arange = (start, stop, inclusive) => [...range(start, stop, inclusive)];
Answered By: maru

For anyone looking for a modern solution
[...Array(n).keys()]

Answered By: Tesohh

A bit of concise, inclusive es6 fun. Codepen demo

var rng=(s,e=null,d=1)=>{
  if(e==null)var[s,e]=[0,s]//missing e? s is e
  if(s>e)d=d<0?d:-d//s,e backwards? might flip sign
  return[...Array(((e-s)/d+1) << 0).keys()].map(x=>d*x+s)
}

rng(3) -> [0,1,2,3]
rng(0,2) -> [0,1,2]
rng(4,6) -> [4,5,6]
rng(3,9,3) -> [3,6,9]
rng(10,27,5) -> [10,15,20,25]
rng(7,null,2) -> [0,2,4,6]
rng(3,-2) -> [3,2,1,0,-1,-2]
rng(3,-2,-2) -> [3,1,-1]
rng(3,-2,2) -> [3,1,-1]
rng(42,42) -> [42]

(Remove the +1 from Array() as is useful/compatible.)

Answered By: kEnobus
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.