How to create range in Javascript

Jason Yu on January 05, 2018

range is a function that basically takes in a starting index and ending index then return a list of all integers from start to end. The most obv... [Read Full]
markdown guide
 

I like this way:

const range = (start, end) => {
    const length = end - start;
    return Array.from({ length }, (_, i) => start + i);
}

As array from works with arrayLike structures and receives a second optional argument mapFn, it's a nice candidate to build range.

 

Yes, excellent solution.

This, slimmed down, version also works...

const range = (start, end, length = end - start) =>
  Array.from({ length }, (_, i) => start + i)

 

const range = (start, end) => Array.from({length: end}, (_, i) => start + 1);

Sorry kerafyrm02, but that does not produce a range.

If I run range with range(1,50) I get [2,2,2,2,2,2,2,2,...]

const range = (start, end) => Array.from({length: end}, (_, i) => start + 1); console.log(range(0,20))

let r = (s, e) => Array.from('x'.repeat(e - s), (_, i) => s + i);

Ok here ya go... even shorter. :D

Rock, paper, scissors... 68 chars, you've nailed it!

Trust me to bump into a js golfer.

Next you'll be telling me that you're dropping the let keyword and just leaving r on the global object!!

 

If you're going to use => might as well go all in... (line break for readability on dev.to)

const range = (start, end) => new Array(end - start + 1)
.fill(undefined).map((_, i) => i + start)

 

I am quite against writing all functions as arrow functions. The reason being that arrow functions are not bind-able and it refers to the this from the outer scope. I feel that it is designed to prevent us writing var self = this; and refer to self in a callback function. So I tend to write arrow functions only when it is a callback or when it is a one-off function. Also if we use arrow function instead of proper function declaration in this case, we will lose the advantage which function hoisting provides. This means range might not be accessible by some earlier parts of our code. But it is all down to personal preference, do tell me what you think.

 

I don't like ambiguity in code. When I'm reviewing code and have to stop and figure out what this is referring to, it makes me frown; with arrow functions I always know exactly what this is. The 'swap this and that' pattern in JS always struck me as a hack. One of the big wins of arrow functions is eliminating the this ambiguity.

I have a similar feeling for hoisting. I understand why it is part of the language, but it always feels like a path to ruin when I have to rely on some behind-the-scenes reshuffling by the interpreter to get my code to run.

All that said, you are correct that there are situations in which arrow functions are not appropriate, which also make me crazy. Having two ways to write functions just adds confusion. "We recommend using arrow functions everyhwere. Oh, sorry, was that a constructor? Write that one this way instead..."

I can't wait to start seeing code in review that's built with templated classes. Maybe ES7 will introduce header files...

Haha! I totally understand what you mean. But still since ES6 introduces class we should really move away from using function as a constructor. As soon as it is consistent in a team then it's fine.

 

Also, I'm a big fan of using variable names that describe what they are (even for internal functions), so (line break for readability on dev.to)
const range = (start, end) => new Array(end - start + 1)
.fill(undefined).map((value, index) => index + start)

 

On the other hand, I am a big fan of Haskell. And we tend to use _ to refer to variables which we don't use in the function. But I do agree with you that having meaningful names is a good practice and could help readability.

Even in Haskell, _ is a bad idea :) Think about the poor intern trying to learn the language while debugging your code. (And it isn't just Haskell...I did a lot of Perl back in the day, which at it best can look like line noise).

BTW since you are a fan of Haskell, I ran across this article the other day that you might enjoy, describing how to structure JS function with Haskell-styl currying.

 

Why not get really wild and crazy :P ?

1st define a unfoldr

const unfoldr = (f, seed, xs = [], next = f(seed)) =>
   next ? unfoldr(f, next[1], xs.concat(next[0])) : xs

then define a range in terms of unfold

const range = (from, to) =>
   unfoldr(seed => seed > to ? false : [seed, seed + 1], from)

excellent article btw, keep them coming :)

 

I just use a one-liner recursive arrow function in ES6; note, it doesn't check but you should only pass it integers else it will never get to a == b so it will blow the stack!



let range = (a, b) => a>b ? range(b, a).reverse() : (a==b ? [a] : range(a, b-1).concat(b));
 
Sloan, the sloth mascot Comment marked as low quality/non-constructive by the community View code of conduct

Are you actually using this in production code? 😂😂

You realise how inefficient this is right? 😂😂

 

I have adapted Namir's method (see comments). And it is probably the most efficient.

 

I liked your recursive generator. How about something like this:

Number.prototype.to = function* (end) {
  const start = this;
  const step = end > start ? 1 : -1;
  const fn = function* (n) {
    let next = start + step * n;
    yield next;
    if (next === end) return;
    yield* fn(n + 1);
  };
  yield start;
  yield* fn(1);   
}

const asc = [...(1).to(5)];
const dsc = [...(5).to(1)];

console.log(asc); // [1, 2, 3, 4, 5]
console.log(dsc); // [5, 4, 3, 2, 1]
 
 

It rather depends, Jason, on what you mean by cooler.

If you only needed range to handle approx. 6000 iterations then I think a recursive range function is pretty cool, but slow:

const range = (s,e) => (s === e) ? [s] : [s, ...range(s+1,e)]

But, if you need a much bigger range and fast executuion then do something like this:

const range = (s=0,e=10,r=[]) =>
  {while(e >= s){ r.push(s); (e>=s)? s++ : s--; } return r }

And if you don't like the arrow function, then it's easy to convert to a standard es5 style function.

 

This is an Awesome string of code you've written. I'm partial to Python3 behavior of range though. Made a few minor adjustments yours. Better to steal like an artist then build from scratch.

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("range requires at least 1 argument, got 0")}    

    if(start >= end) return                                                                                                                                     
    yield start    
    yield* range(parseInt(start + step), end, step)    
}

// Use Cases
console.log([...range(5)])

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

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

Might be inefficient to create an array for large ranges, and only supports range of integers (no dates). Have you seen Ruby's Range class? ruby-doc.org/core-2.2.0/Range.html
You could still implement toArray() and offer a covers() function.

 

Yes! You are right! I will add a generator implementation as well which should solve this problem! ;D

 

What do you think about it? I have implemented.

 

The for loop is by far the easiest to read and also by far the most efficient. Your last example was literally more than 15 times slower than the for loop. Far too many developers these days are trying to get all fancy when the simple solution is much better.

 
function range(start, end) {
  return [...Array((end - start) + 1).keys()].map(val => val + start);
}
 

I think this code is not going to work with float values. like generating a range like [0.01, 0.02, 0.03 ...]

 

Jason, just curious, what's your concern about looping? I don't have a reference, but I would think that the various interpreters (especially V8) would optimize / unroll them.

 

It's just personal taste I guess. I think code with less loops are more readable.

 
 

Just a variable name. Has no special meaning to the language.

code of conduct - report abuse