JavaScript ranges

If you have some experience with Ruby, you’re probably familiar with the Ruby range syntax:

(7..11)

A Ruby range is an enumerable object with a starting and ending value. The range above will iterate from 7 up to 11. Super easy!

Let’s see what that looks like in JavaScript:

for (let i = 7; i <= 11; i++) {
	//
}

Oh, right. JavaScript doesn’t have ranges.

But wait… if the above for loop gets the job done, does JavaScript even need ranges? After all, Ruby is known for having a lot of shiny features that might secretly be bad ideas.

Well, check this out:

(7..11).include? 9 # true

my_range = (13..31)
my_range.include? 12 # false
my_range.include? 30 # true

Notice how the range can have its own methods because it’s an actual object? And we can store it and use it later? We can’t do that with JavaScript, and sometimes shiny new features like ranges have an actual purpose.

In this case, an actual range object makes it easier to read and manage our code since we are encapsulating the starting and ending value into a single object.

It would be nice if we could just do this in JavaScript:

for (let i of Range(7, 11)) {
  console.log(i)
}

const myRange = Range(4, -8)
if (myRange.includes(-1)) {
  console.log('We got a negative one')
}

Fortunately, it’s not hard to make our own JavaScript ranges. There are different ways of approaching this problem, including actual JavaScript iterators and generators.

To keep things simple, I’m just going to define a function that creates arrays of integers based on the from and to arguments I pass in:

const Range = (from, to) => {
  const increment = from < to ? 1 : -1
  let difference = Math.abs(to - from) + 1
  const result = []
  while (difference) {
    result.push(from)
    from += increment
    difference--
  }
  return result
}

Since this allocates an entire array in memory, it could get expensive if you are creating many ranges or very large ranges. You should definitely consider whether a regular for loop will get the job done before using this snippet.

On the other hand, if you have a script you pull out once a year that iterates over arbitrary ranges of integers, this is a great way to make it easier to revisit and maintain.