clwen
clwen

Reputation: 20919

JavaScript function similar to Python range()

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;
}

Upvotes: 165

Views: 123938

Answers (30)

fero
fero

Reputation: 528

This is my solution, I post here even if there are already a lots of answers, because I think it is smart and coincise as well as understandable by human being:

let range = (start, stop, step)  => [
   ...Array((stop?stop:start*2)-start).keys().map(x => { 
              if (stop) x+=start; 
              if (!((x-start)%step)) return x})].filter(x => (x !== undefined))

My tests:

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

Does not work with descending arrays:

rang(5, -1, 2) -> Uncaught RangeError: Invalid Array Length

Upvotes: 0

N Djel Okoye
N Djel Okoye

Reputation: 1078

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, step)];
}

refer to here for more.

Upvotes: 1

kEnobus
kEnobus

Reputation: 66

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

Upvotes: 0

Tesohh
Tesohh

Reputation: 141

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

Upvotes: 7

maru
maru

Reputation: 11

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)];

Upvotes: 0

Denis Giffeler
Denis Giffeler

Reputation: 1509

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

Upvotes: 0

mh-firouzjah
mh-firouzjah

Reputation: 844

/**
 * 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)]);

Upvotes: 0

Tyler Liu
Tyler Liu

Reputation: 20366

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]

Upvotes: -1

Sanuja Methmal
Sanuja Methmal

Reputation: 1

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.

Upvotes: -2

Mohammadhossein
Mohammadhossein

Reputation: 61

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

Upvotes: 4

MSA
MSA

Reputation: 277

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

Upvotes: 1

janka102
janka102

Reputation: 1019

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

Upvotes: 48

Otto
Otto

Reputation: 2066

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

Upvotes: 1

Keyvan
Keyvan

Reputation: 873

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

Upvotes: 8

Ricky Sahu
Ricky Sahu

Reputation: 24329

Here's how i do it

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

output

0
1
2
3
4

Upvotes: 0

Rob Kwasowski
Rob Kwasowski

Reputation: 2780

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

Upvotes: 2

IliasT
IliasT

Reputation: 4301

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

Upvotes: 6

Steve Brownlee
Steve Brownlee

Reputation: 113

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.

Upvotes: 2

hcotta
hcotta

Reputation: 423

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

Upvotes: 1

user1969453
user1969453

Reputation:

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

Upvotes: 218

Mark Amery
Mark Amery

Reputation: 154934

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);
=> []

Upvotes: 30

MattCochrane
MattCochrane

Reputation: 3090

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]

Upvotes: 3

elayira
elayira

Reputation: 11

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.

Upvotes: 0

georg
georg

Reputation: 215009

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

Upvotes: 36

mrFunkyWisdom
mrFunkyWisdom

Reputation: 11

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}`)
}

Upvotes: 0

MCH
MCH

Reputation: 1032

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

Upvotes: 24

le_m
le_m

Reputation: 20238

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.

Upvotes: 6

Dmitrii Mikhailov
Dmitrii Mikhailov

Reputation: 5231

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

Upvotes: 11

bigOmega  ツ
bigOmega ツ

Reputation: 371

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]

Upvotes: 5

Volv
Volv

Reputation: 73

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
}

Upvotes: 6

Related Questions