1752

In PHP, you can do...

range(1, 3); // Array(1, 2, 3)
range("A", "C"); // Array("A", "B", "C")

That is, there is a function that lets you get a range of numbers or characters by passing the upper and lower bounds.

Is there anything built-in to JavaScript natively for this? If not, how would I implement it?

12
  • 2
    Prototype.js has the $R function, but other than that I don't really think so. Commented Oct 9, 2010 at 2:42
  • This (related) question has some excellent answers: stackoverflow.com/questions/6299500/… Commented Feb 25, 2015 at 14:47
  • 2
    When lover bound is zero this oneliner: Array.apply(null, { length: 10 }).map(eval.call, Number) Commented Jul 25, 2016 at 16:32
  • 1
    Possible duplicate of Create a JavaScript array containing 1...N Commented Mar 28, 2019 at 20:08
  • 1
    No, but you can define the function using: const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step)); (see 'Sequence generator (range)' from MSDN) NOTE: This function only works if all parameters are specified (ie. range(1,5,1) produces the array [1,2,3,4,5], but range(1,5) produces an empty array) Commented Feb 15, 2021 at 8:12

92 Answers 92

8

As far as generating a numeric array for a given range, I use this:

function range(start, stop)
{
    var array = [];

    var length = stop - start; 

    for (var i = 0; i <= length; i++) { 
        array[i] = start;
        start++;
    }

    return array;
}

console.log(range(1, 7));  // [1,2,3,4,5,6,7]
console.log(range(5, 10)); // [5,6,7,8,9,10]
console.log(range(-2, 3)); // [-2,-1,0,1,2,3]

Obviously, it won't work for alphabetical arrays.

Sign up to request clarification or add additional context in comments.

Comments

8

You can use following one-liner to keep things short and simple

var start = 4;
var end = 20;
console.log(Array(end - start + 1).fill(start).map((x, y) => x + y));

Comments

8

Use this. It creates an array with given amount of values (undefined), in the following example there are 100 indexes, but it is not relevant as here you need only the keys. It uses in the array, 100 + 1, because the arrays are always 0 index based. So if it's given 100 values to generate, the index starts from 0; hence the last value is always 99 not 100.

range(2, 100);

function range(start, end) {
    console.log([...Array(end + 1).keys()].filter(value => end >= value && start <= value ));
}

Comments

6

Using Harmony generators, supported by all browsers except IE11:

var take = function (amount, generator) {
    var a = [];

    try {
        while (amount) {
            a.push(generator.next());
            amount -= 1;
        }
    } catch (e) {}

    return a;
};

var takeAll = function (gen) {
    var a = [],
        x;

    try {
        do {
            x = a.push(gen.next());
        } while (x);
    } catch (e) {}

    return a;
};

var range = (function (d) {
    var unlimited = (typeof d.to === "undefined");

    if (typeof d.from === "undefined") {
        d.from = 0;
    }

    if (typeof d.step === "undefined") {
        if (unlimited) {
            d.step = 1;
        }
    } else {
        if (typeof d.from !== "string") {
            if (d.from < d.to) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        } else {
            if (d.from.charCodeAt(0) < d.to.charCodeAt(0)) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        }
    }

    if (typeof d.from === "string") {
        for (let i = d.from.charCodeAt(0); (d.step > 0) ? (unlimited ? true : i <= d.to.charCodeAt(0)) : (i >= d.to.charCodeAt(0)); i += d.step) {
            yield String.fromCharCode(i);
        }
    } else {
        for (let i = d.from; (d.step > 0) ? (unlimited ? true : i <= d.to) : (i >= d.to); i += d.step) {
            yield i;
        }
    }
});

Examples

take

Example 1.

take only takes as much as it can get

take(10, range( {from: 100, step: 5, to: 120} ) )

returns

[100, 105, 110, 115, 120]

Example 2.

to not neccesary

take(10, range( {from: 100, step: 5} ) )

returns

[100, 105, 110, 115, 120, 125, 130, 135, 140, 145]

takeAll

Example 3.

from not neccesary

takeAll( range( {to: 5} ) )

returns

[0, 1, 2, 3, 4, 5]

Example 4.

takeAll( range( {to: 500, step: 100} ) )

returns

[0, 100, 200, 300, 400, 500]

Example 5.

takeAll( range( {from: 'z', to: 'a'} ) )

returns

["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"]

Comments

6

... more range, using a generator function.

function range(s, e, str){
  // create generator that handles numbers & strings.
  function *gen(s, e, str){
    while(s <= e){
      yield (!str) ? s : str[s]
      s++
    }
  }
  if (typeof s === 'string' && !str)
    str = 'abcdefghijklmnopqrstuvwxyz'
  const from = (!str) ? s : str.indexOf(s)
  const to = (!str) ? e : str.indexOf(e)
  // use the generator and return.
  return [...gen(from, to, str)]
}

// usage ...
console.log(range('l', 'w'))
//=> [ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' ]

console.log(range(7, 12))
//=> [ 7, 8, 9, 10, 11, 12 ]

// first 'o' to first 't' of passed in string.
console.log(range('o', 't', "ssshhhooooouuut!!!!"))
// => [ 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 't' ]

// only lowercase args allowed here, but ...
console.log(range('m', 'v').map(v=>v.toUpperCase()))
//=> [ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' ]

// => and decreasing range ...
console.log(range('m', 'v').map(v=>v.toUpperCase()).reverse())

// => ... and with a step
console.log(range('m', 'v')
          .map(v=>v.toUpperCase())
          .reverse()
          .reduce((acc, c, i) => (i % 2) ? acc.concat(c) : acc, []))

// ... etc, etc.

Hope this is useful.

Comments

6

My codegolfing coworker came up with this (ES6), inclusive:

(s,f)=>[...Array(f-s+1)].map((e,i)=>i+s)

non inclusive:

(s,f)=>[...Array(f-s)].map((e,i)=>i+s)

Comments

6

Javascript provides a function to create and fill an array from given values, receiving the container array and a map function as parameters:

let arr = Array.from(SOURCE_ARRAY, MAP_FUNCTION);

Since the MAP_FUNCTION provides the value and index for the iteration, it's possible to create an empty array (SOURCE_ARRAY) and fill it using indexes, like this (supossing 10 is your desired lenght):

let arr = Array.from(Array(10), (n, index) => index);

Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].

Comments

5

you can use lodash function _.range(10) https://lodash.com/docs#range

Comments

5

Complete ES6 implementation using range([start, ]stop[, step]) signature:

function range(start, stop, step=1){
  if(!stop){stop=start;start=0;}
  return Array.from(new Array(int((stop-start)/step)), (x,i) => start+ i*step)
}

If you want automatic negative stepping, add

if(stop<start)step=-Math.abs(step)

Or more minimalistically:

range=(b, e, step=1)=>{
  if(!e){e=b;b=0}
  return Array.from(new Array(int((e-b)/step)), (_,i) => b<e? b+i*step : b-i*step)
}

If you have huge ranges look at Paolo Moretti's generator approach

1 Comment

Replace !stop with typeof stop === 'undefined', then replace int with Math.floor, and add a check if (start > stop && step > 0) (otherwise, range(-3, -10) throws an exception instead of doing something sane (either flipping the sign of step or returning [])). Otherwise, good!
5

You can also use a generator to produce the sequence. The difference is that each value in the sequence is lazy loaded. spread operator and for of works for the result. The asterisk symbols makes the function to be a generator.

const range = function*(from,to) {   
    for(let i = from; i <= to; i++) yield I;   
};   

[...range(3,5)]// => [3, 4, 5]

Comments

5

Op asked for a range, say range(3, 10), so it can be

[...[...Array(10-3).keys()].map(i => i+3)]

returns

[3, 4, 5, 6, 7, 8, 9]

1 Comment

the external brackets can be removed safely: ` [...Array(10-3).keys()].map(i=>i+3) == [ 3, 4, 5, 6, 7, 8, 9 ] `
4

Here's a nice short way to do it in ES6 with numbers only (don't know its speed compares):

Array.prototype.map.call(' '.repeat(1 + upper - lower), (v, i) => i + lower)

For a range of single characters, you can slightly modify it:

Array.prototype.map.call(' '.repeat(1 + upper.codePointAt() - lower.codePointAt()), (v, i) => String.fromCodePoint(i + lower.codePointAt()));

1 Comment

A slightly less greedy yet still concise alternative, Array(1 + upper - lower).fill().map((v, i) => i + lower)
4

There's an npm module bereich for that ("bereich" is the German word for "range"). It makes use of modern JavaScript's iterators, so you can use it in various ways, such as:

console.log(...bereich(1, 10));
// => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

const numbers = Array.from(bereich(1, 10));
// => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

for (const number of bereich(1, 10)) {
  // ...
}

It also supports descending ranges (by simply exchanging min and max), and it also supports steps other than 1.

Disclaimer: I am the author of this module, so please take my answer with a grain of salt.

Comments

4

You can also do the following:

const range = Array.from(Array(size)).map((el, idx) => idx+1).slice(begin, end);

Comments

4

I did a review of the answers here, and noticed the following points:

  • JavaScript doesn't have a built-in solution for this problem
  • Some answers generate an Array of the correct size but of the wrong values
    • e.g. let arr = Array.from( {length:3} ); // gives [null,null,null]
  • Then, a mapping function is used to replace the wrong values with the correct values
    • e.g. arr.map( (e,i) => i ); // gives [0,1,2]
  • Math is required to shift the number range to satisfy the from..to requirement.
    • e.g. arr.map( (e,i) => i+1 ); // gives [1,2,3]
  • For the string version of the problem, both charCodeAt and fromCharCode was needed to map the string to a number then back to a string again.
    • e.g. arr.map( (e,i) => String.fromCharCode( i+"A".charCodeAt(0) ); // gives ["A","B","C"]

There are some simple to some fancy implementations based on some or all of the above. The answers got complicated when they try to pack both the integer and string solutions into a single function. For me, I elected to implement integer and string solutions in their own functions. I justify this because, in practice, you will know what your use case is and you will use the appropriate function directly. I also supply the wrapper function if you want to call it indirectly.

let rangeInt = (from,to) => Array.from( { length: to-from+1 }, (e, i) => i + from );
let rangeChar = (from,to) => Array.from( { length: to.charCodeAt(0)-from.charCodeAt(0)+1 }, (e,i) => String.fromCharCode(i+from.charCodeAt(0)) );
let range = (from,to) =>
    (typeof(from) === 'string' && typeof(to) === 'string') 
    ? rangeChar(from,to)
    : (!to) ? rangeInt(0,from-1) : rangeInt(from,to);

console.log( rangeInt(1,3) ); // gives [1,2,3]
console.log( rangeChar("A","C") ); // gives ["A","B","C"]
console.log( range(1,3) ); // gives [1,2,3]
console.log( range("A","C") ); // gives ["A","B","C"]
console.log( range(3) ); // gives [0,1,2]

Comments

3

I was surprised to come across this thread and see nothing like my solution (maybe I missed an answer), so here it is. I use a simple range function in ES6 syntax :

// [begin, end[
const range = (b, e) => Array.apply(null, Array(e - b)).map((_, i) => {return i+b;});

But it works only when counting forward (ie. begin < end), so we can modify it slightly when needed like so :

const range = (b, e) => Array.apply(null, Array(Math.abs(e - b))).map((_, i) => {return b < e ? i+b : b-i;});

1 Comment

Use [...Array(e-b)] while you are at it.
3

None of the examples had tests, implementation for step with an option to produce decreasing values.

export function range(start = 0, end = 0, step = 1) {
    if (start === end || step === 0) {
        return [];
    }

    const diff = Math.abs(end - start);
    const length = Math.ceil(diff / step);

    return start > end
        ? Array.from({length}, (value, key) => start - key * step)
        : Array.from({length}, (value, key) => start + key * step);

}

Tests:

import range from './range'

describe('Range', () => {
    it('default', () => {
        expect(range()).toMatchObject([]);
    })

    it('same values', () => {
        expect(range(1,1)).toMatchObject([]);
    })

    it('step=0', () => {
        expect(range(0,1,0)).toMatchObject([]);
    })

    describe('step=1', () => {
        it('normal', () => {
            expect(range(6,12)).toMatchObject([6, 7, 8, 9, 10, 11]);
        })

        it('reversed', () => {
            expect(range(12,6)).toMatchObject([12, 11, 10, 9, 8, 7]);
        })
    })

    describe('step=5', () => {

        it('start 0 end 60', () => {
            expect(range(0, 60, 5)).toMatchObject([0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]);
        })

        it('reversed start 60 end -1', () => {
            expect(range(55, -1, 5)).toMatchObject([55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0]);
        })
    })
})

Comments

3

Keeping it simple:

// Generator
function* iter(a, b, step = 1) {
  for (let i = b ? a : 0; i < (b || a); i += step) {
    yield i
  }
}

const range = (a, b, step = 1) =>
  typeof a === 'string'
    ? [...iter(a.charCodeAt(), b.charCodeAt() + 1)].map(n => String.fromCharCode(n))
    : [...iter(a, b, step)]

range(4) // [0, 1, 2, 3]
range(1, 4) // [1, 2, 3]
range(2, 20, 3) // [2, 5, 8, 11, 14, 17]
range('A', 'C') // ['A', 'B', 'C']

Comments

3

In order to work where either given number could be larger I wrote this:

function getRange(start, end) {
  return Array.from({
    length: 1 + Math.abs(end - start)
  }, (_, i) => end > start ? start + i : start - i);
}

Comments

3

You can create your own es6 range version

const range = (min, max) => {
  const arr = Array(max - min + 1)
    .fill(0)
    .map((_, i) => i + min);
  return arr;
}

console.log(range(0,5));

console.log(range(2,8))

Comments

3

Here is the way to implment your own iterable range function.

// implementing range
function range(start, end){
    return {
      from: start,
      to: end,

      [Symbol.iterator]() { 
        this.current = this.from;
        return this;
      },

      next() { 
        if (this.current <= this.to) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };

}

// iterate over each value
for (let num of range(1,5)) {
  console.log(num); // 1, 2, 3, 4, 5
}

Comments

2

A recursive solution to generating integer array within bounds.

function intSequence(start, end, n = start, arr = []) {
  return (n === end) ? arr.concat(n)
    : intSequence(start, end, start < end ? n + 1 : n - 1, arr.concat(n));
}

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

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

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

Comments

2

For function that behaves like python range() function, use this:

function range(min=0, max=null){
    if(max === null){
        max=min;
        min=0;
    }
    var rg=[...Array(max).keys()];
    return rg.slice(min,max);
}   

Comments

2

My favorite is the generator generateRange with another function getRange to run the generator. An advantage of this compared to many other solutions is that unnecessary arrays are not created multiple times.

function* generateRange(start, end, step = 1) {
    let current = start;
    while (start < end ? current <= end : current >= end) {
        yield current;
        current = start < end ? current + step : current - step;
    }
}

function getRange(start, end, step = 1) {
    return [...generateRange(start, end, step)];
}

console.log(getRange(0, 5)) // [ 0, 1, 2, 3, 4, 5 ]
console.log(getRange(10, 0, 2)) // [ 10, 8, 6, 4, 2, 0 ]

1 Comment

For others info, usage og generateRange here: for (let i of generateRange(5,10)){console.log(i)}
2

The closest I have found is with the radash library:

import { range } from 'radash'

for (let i of range(1, 5)) {
    console.log(i)
}

See documentation here:

range-Radash

Comments

2

A range function with the step (where step > 0) could be,

const range = (start, end, step) => [...Array(end-start).keys()]    
                                    .filter((e) => e % step == 0)
                                    .map((e) => e + start)
                                
console.log(range(40, 50, 3));

output: [ 40, 43, 46, 49 ]

Comments

1

I found a JS range function equivalent to the one in PHP, and works amazingly great here. Works forward & backward, and works with integers, floats and alphabets!

function range(low, high, step) {
  //  discuss at: http://phpjs.org/functions/range/
  // original by: Waldo Malqui Silva
  //   example 1: range ( 0, 12 );
  //   returns 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  //   example 2: range( 0, 100, 10 );
  //   returns 2: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
  //   example 3: range( 'a', 'i' );
  //   returns 3: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
  //   example 4: range( 'c', 'a' );
  //   returns 4: ['c', 'b', 'a']

  var matrix = [];
  var inival, endval, plus;
  var walker = step || 1;
  var chars = false;

  if (!isNaN(low) && !isNaN(high)) {
    inival = low;
    endval = high;
  } else if (isNaN(low) && isNaN(high)) {
    chars = true;
    inival = low.charCodeAt(0);
    endval = high.charCodeAt(0);
  } else {
    inival = (isNaN(low) ? 0 : low);
    endval = (isNaN(high) ? 0 : high);
  }

  plus = ((inival > endval) ? false : true);
  if (plus) {
    while (inival <= endval) {
      matrix.push(((chars) ? String.fromCharCode(inival) : inival));
      inival += walker;
    }
  } else {
    while (inival >= endval) {
      matrix.push(((chars) ? String.fromCharCode(inival) : inival));
      inival -= walker;
    }
  }

  return matrix;
}

And here is the minified version:

function range(h,c,b){var i=[];var d,f,e;var a=b||1;var g=false;if(!isNaN(h)&&!isNaN(c)){d=h;f=c}else{if(isNaN(h)&&isNaN(c)){g=true;d=h.charCodeAt(0);f=c.charCodeAt(0)}else{d=(isNaN(h)?0:h);f=(isNaN(c)?0:c)}}e=((d>f)?false:true);if(e){while(d<=f){i.push(((g)?String.fromCharCode(d):d));d+=a}}else{while(d>=f){i.push(((g)?String.fromCharCode(d):d));d-=a}}return i};

Comments

1

For a more ruby-like approach with good backward compatibility:

range([begin], end = 0) where begin and end are numbers

var range = function(begin, end) {
  if (typeof end === "undefined") {
    end = begin; begin = 0;
  }
  var result = [], modifier = end > begin ? 1 : -1;
  for ( var i = 0; i <= Math.abs(end - begin); i++ ) {
    result.push(begin + i * modifier);
  }
  return result;
}

Examples:

range(3); //=> [0, 1, 2, 3]
range(-2); //=> [0, -1, -2]
range(1, 2) //=> [1, 2]
range(1, -2); //=> [1, 0, -1, -2]

1 Comment

typeof on a variable guaranteed to be set can be replaced with a strict equality check with undefined or void 0 if you're paranoid and not in strict mode.
1

Here is my solution that mimics Python. At the bottom you can find some examples how to use it. It works with numbers, just like Python's range:

var assert = require('assert');    // if you use Node, otherwise remove the asserts

var L = {};    // L, i.e. 'list'

// range(start, end, step)
L.range = function (a, b, c) {
    assert(arguments.length >= 1 && arguments.length <= 3);
    if (arguments.length === 3) {
        assert(c != 0);
    }

    var li = [],
        i,
        start, end, step,
        up = true;    // Increasing or decreasing order? Default: increasing.

    if (arguments.length === 1) {
        start = 0;
        end = a;
        step = 1;
    }

    if (arguments.length === 2) {
        start = a;
        end = b;
        step = 1;
    }

    if (arguments.length === 3) {
        start = a;
        end = b;
        step = c;
        if (c < 0) {
            up = false;
        }
    }

    if (up) {
        for (i = start; i < end; i += step) {
            li.push(i);
        }
    } else {
        for (i = start; i > end; i += step) {
            li.push(i);
        }
    }

    return li;
}

Examples:

// range
L.range(0) -> []
L.range(1) -> [0]
L.range(2) -> [0, 1]
L.range(5) -> [0, 1, 2, 3, 4]

L.range(1, 5) -> [1, 2, 3, 4]
L.range(6, 4) -> []
L.range(-2, 2) -> [-2, -1, 0, 1]

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

Comments

1
// range()              0..10, step=1
// range(max)           0..max, step=1
// range(min,max)       min..max, step=1
// range(min,step,max)  min..max, step=step
// Use:
// console.log(...range(3));
// Array.from(range(5))
// [...range(100)]
// for (const v of range(1,10)) { ... 

function* range(...args) {
    let [min, step, max] = {
        0: [0, 1, 10],
        1: [0, args[0] >= 0 ? 1 : -1, args[0]],
        2: [args[0], args[1] >= args[0] ? 1 : -1, args[1]],
        3: args,
    }[args.length] || [];
    if (min === undefined) throw new SyntaxError("Too many arguments");
    let x = min;
    while (step >= 0 ? x < max : x > max) {
        yield x;
        x += step
    }
}
console.log(...range());      // 0 1 2 3 4 5 6 7 8 9
console.log(...range(3));     // 0 1 2
console.log(...range(2, 5));  // 2 3 4
console.log(...range(5, 2));  // 5 4 3
console.log(...range(3, -3)); // 3 2 1 0 -1 -2
console.log(...range(-3, 3)); // -3 -2 -1 0 1 2
console.log(...range(-5, -2));// -5 -4 -3
console.log(...range(-2, -5));// -2 -3 -4

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.