Built-in-like Range in JavaScript

didof

Francesco Di Donato

Posted on October 6, 2021

Built-in-like Range in JavaScript

Premise

Make it possible to generate any range of integers with built-in-like syntax.

Motivation?
Honestly, none. Zero. Except for fun & study.


Basic functionality

You start by overriding the prototype of Number with itself, but proxed.

Object.setPrototypeOf(
  Number.prototype,
  new Proxy(Number.prototype, {
    // ...
  })
)
Enter fullscreen mode Exit fullscreen mode

In this way, any normal operations related to the prototype are not lost.

In the proxy you listen for access to any property via a getter. The third argument (receiver) is the "object", in this case the number itself - you call it start. It's already the right type, number.

The second argument corresponds to the name of the property, its typeof is indeed string.

Object.setPrototypeOf(
  Number.prototype,
  new Proxy(Number.prototype, {
    get(_, _end, start) {
      // _end -> '182' (typeof string)
      // start -> 42 (typeof number)
    },
  })
)

(42)[182]
Enter fullscreen mode Exit fullscreen mode

It is sufficient to use parseInt and, if it still isNaN just throw an error/warning. Or just ignore it silently and fallback by returning start.

let end = parseInt(_end)
if (isNaN(end)) {
  // warning or error
  // eventually, fallback
  return start
}
Enter fullscreen mode Exit fullscreen mode

Assured that the typeof end is also number, you can proceed to generate the range.

return Array(end - start + 1)
  .fill()
  .map((_, i) => start + i)
Enter fullscreen mode Exit fullscreen mode

Basic functionality is complete. Now the following code is perfectly valid.

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

To make it not-end-inclusive, use
Array(end - start) instead of Array(end - start + 1).


Reverse range

To be able to do something like the following...

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

Check if start > end and if so swap both. Don't forget to sort the result in descending order.

The code is self-explanatory.

Object.setPrototypeOf(
  Number.prototype,
  new Proxy(Number.prototype, {
    get(_, _end, start) {
      // where (start)[_end]

      let end = parseInt(_end)
      if (isNaN(end)) {
        // warning or error
        // eventually, fallback
        return start
      }

      // sort behaviour - default ASC
      let s = +1

      if (start > end) {
        // swap
        let tmp = start
        start = end
        end = tmp

        // sort behaviour - DESC
        s = -1
      }

      // generate range
      return Array(end - start + 1)
        .fill()
        .map((_, i) => start + i)
        .sort(() => s)
    },
  })
)
Enter fullscreen mode Exit fullscreen mode

Result

42             // 42
(0)[5]         // [0, 1, 2, 3, 4, 5]
(0)['foo']     // #fallback -> 0
(3)[7]         // [3, 4, 5, 6, 7]
(8)[3]         // [8, 7, 6, 5, 4, 3]
Enter fullscreen mode Exit fullscreen mode

Couldn't I have done the same thing with a range function?
Yes, probably you should do it with a function.

Let this be a mental exercise and a way of making friends with the concept of prototype and proxy.

If you want to chat about nerdy things or just say hi, you can find me here:

💖 💪 🙅 🚩
didof
Francesco Di Donato

Posted on October 6, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related