DEV Community

YCM Jason
YCM Jason

Posted on • Edited on

JavaScript Range How to create range in Javascript

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 obvious way would be using a for loop.

function range(start, end) {
    var ans = [];
    for (let i = start; i <= end; i++) {
        ans.push(i);
    }
    return ans;
}
Enter fullscreen mode Exit fullscreen mode

As a fan of FP, let's come up with an recursive solution. So the base case is obviously when the start and end are the same, the answer would simply be [start].

function range(start, end) {
    if(start === end) return [start];
    // recursive case
}
Enter fullscreen mode Exit fullscreen mode

Now take the leap of faith, assume that range(start, end) will just work. Then how do we solve the problem range(start, end)? Simple! Just do [start, ...range(start + 1, end)].

So combining both, we get

function range(start, end) {
    if(start === end) return [start];
    return [start, ...range(start + 1, end)];
}
Enter fullscreen mode Exit fullscreen mode

A lot more elegant than the for-loop solution in my opinion. But we could even go further if we use new Array(n) which creates an array with n elements.

If we have an n element list, we could build a range from it by mapping each element to its index, i.e. arr.map((_, i) => i).

However, according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Description, map will not call for unassigned element. This mean we need to initialise the new Array(n) before mapping. One standard technique is to use fill. The final result is the following.

function range(start, end) {
    return (new Array(end - start + 1)).fill(undefined).map((_, i) => i + start);
}
Enter fullscreen mode Exit fullscreen mode

We could also make use of Array.from to create range:

function range(start, end) {
  return Array.from({ length: end - start + 1 }, (_, i) => i)
}
Enter fullscreen mode Exit fullscreen mode

Thank you Step for mentioning about efficiency when handling large ranges, which essentially build a huge array. We could have a more efficient way of doing this by using generators.

function* range(start, end) {
    for (let i = start; i <= end; i++) {
        yield i;
    }
}
Enter fullscreen mode Exit fullscreen mode

We could use this generator in a for...of loop (which would be very efficient) or use an array spread to retrieve all values (note that this essentially builds the array which is essentially the same as the non-generator approaches.)

for (i of range(1, 5)) {
    console.log(i);
}
/* Output
 * 1 2 3 4 5 */

[...range(1, 5)] // [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Since I always try to avoid for loops, we could also define the generator recursively as follows.

function* range(start, end) {
    yield start;
    if (start === end) return;
    yield* range(start + 1, end);
}
Enter fullscreen mode Exit fullscreen mode

Could you think of some cooler method to achieve this?

Latest comments (59)

Collapse
 
nassehk profile image
Nassehk

Python world has experimented with creating the whole array vs generator. They landed on the generator. In Python 2 they have range for whole array and xrange for generator. In python 3 they dropped the array all together and use range function to return a generator.

Collapse
 
furf profile image
Dave Furfero • Edited

let's go both ways!

function range(start, end) {
  const sign = start > end ? -1 : 1;
  return Array.from(
    { length: Math.abs(end - start) + 1 },
    (_, i) => start + i * sign
  );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ycmjason profile image
YCM Jason

oooooo maybe with Math.sign?

Collapse
 
jclemens24 profile image
Jordan Clemens

This is how I went about creating a different solution although it is only really beneficial by calling (value in target) and really only counts as a "typeof range" :) Just bored and playing around with proxies.

console.log(...generateRange());

let range = {
    start: 1,
    end: 10,
};

range = new Proxy(range, {
    has(target: typeof range, prop: string | symbol | number) {
        if (typeof prop === 'number') {
            return prop >= target.start && prop <= target.end;
        } else {
            return Number(prop) >= target.start && Number(prop) <= target.end;
        }
    },
});

console.log(5 in range);
console.log(50 in range);

Enter fullscreen mode Exit fullscreen mode
Collapse
 
alexandruorosspark profile image
Oros Alexandru
function range(start: number, end: number, step = 1) {
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      if (start < end) {
        start = start + step;
        return {done: false, value: start - step}
      }
      return {done: true, value: end}
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
bigzude profile image
Big-Zude

After reading through this helpful discussion this what I came up with, I added a condition if the start point of the range is greater than the end point. This my very first contribution

function range(s, e) {
  var arr = [];
  for (let i = s; i <= e; i++) {
      arr.push(i);
  }
  return  s > e ? Array(s - e + 1).fill(0).map((x, y) => - y + s):arr;
}
console.log(range(10,1)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ayobamimichael profile image
AyobamiMichael

Thanks bro, it was helpful.

Collapse
 
andresporras3423 profile image
Oscar Andrรฉs Russi Porras

Like this:
[...Array().keys()];
this way you get the range from 0 til -1

Collapse
 
mihailoff profile image
George Mihailov

Why nobody is wondering why this is not part of the standard library?

php.net/manual/en/function.range.php
ruby-doc.org/core-2.5.1/Range.html
docs.python.org/3.3/library/stdtyp...

Collapse
 
danielboll profile image
Daniel Boll

I'm a little late for the discussion ๐Ÿ˜… but I found the different ways interesting, I ended up getting this one. Is it valid?

const range = (start, end, length = end - start + 1) => [...Array(length).keys()].map(d => d + start) 
Collapse
 
ycmjason profile image
YCM Jason

Looks great to me!

Collapse
 
josefrichter profile image
Josef Richter

The recursive one is really nice. Can be shortened to:

const range = (s, e) => e > s ? [s, ...range(s + 1, e)] : [s];
Enter fullscreen mode Exit fullscreen mode

Also this is another approach, + easier to handle descending sequences too:

const range = (s,e) => e > s ? Array(e - s + 1).fill(0).map((x, y) => y + s) : Array(s - e + 1).fill(0).map((x, y) => - y + s);
Enter fullscreen mode Exit fullscreen mode

Some comments may only be visible to logged-in visitors. Sign in to view all comments.