DEV Community

Cover image for Built-in-like Range in JavaScript
Francesco Di Donato
Francesco Di Donato

Posted on

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:

Top comments (16)

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

I'm planning to build something similar, but with 'safe' monkey patching using Symbols. I think I'll end up with a syntax something like:

4[to](7) // [4, 5, 6, 7]
[ ...1[to](3), ...10[to](15) ] // [1, 2, 3, 10, 11, 12, 13, 14, 15]

(4)[to](7) // alternative way to write it
Enter fullscreen mode Exit fullscreen mode

Or maybe (not sure this is possible safely):

4[to(7)] // [4, 5, 6, 7]
[ ...1[to(3)], ...10[to(15)] ] // [1, 2, 3, 10, 11, 12, 13, 14, 15]
Enter fullscreen mode Exit fullscreen mode

Which syntax do you prefer? (I'm leaning towards the former)

Collapse
 
jonrandy profile image
Jon Randy 🎖️

Could even expand it to:

1[to](19, {step: 2})) // [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
Enter fullscreen mode Exit fullscreen mode

etc.

Collapse
 
didof profile image
Francesco Di Donato • Edited

It's an amazing idea, Jon. I find the second option more readable. Or, at least, it feels "righter". But I'm not sure it could work since a function cannot be used as a key in a plain object.
Anyway, I'd like to work on it, together. Would you like to?

Thread Thread
 
jonrandy profile image
Jon Randy 🎖️

I already did a POC in about 5 lines of code - works perfectly

Thread Thread
 
jonrandy profile image
Jon Randy 🎖️

Still trying to think of a way to make the second syntax work without unsafe monkey patching. I'm not sure it's possible. My POC uses the a[to](b) syntax. Basically, to is a method on the number prototype... nothing particularly fancy going on

Thread Thread
 
jonrandy profile image
Jon Randy 🎖️

I think there is a way to do the 2nd syntax... I'll get back to you

Thread Thread
 
jonrandy profile image
Jon Randy 🎖️ • Edited

It works 👍 And it's safe

Thread Thread
 
didof profile image
Francesco Di Donato

I'm glad to read it. If you like to share I'd love to see it!

Collapse
 
jonrandy profile image
Jon Randy 🎖️

I noticed in your header image, you have (3)(7) - that's not gonna work

Collapse
 
didof profile image
Francesco Di Donato

Ops, thank you man! This is what happens when you write posts at 2 a.m. :)
I'm gonna fix it

Collapse
 
Sloan, the sloth mascot
Comment deleted
 
poker profile image
Poker9x

shshshsh

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀

This is too much magic for me, don't comment to argue, I don't like it I won't change my mind but you can like it.

Collapse
 
didof profile image
Francesco Di Donato

Hi Adam, I understand you point of view. As I stated in the post, this is something superfluous, a dev can definitely live without it :)

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀

It's not that, I woke up on the wrong side of the bed and I saw prototype of native primitive type being modified and it's just not my bag. Sorry if this came across in any other way but grumpy 😜

Collapse
 
didof profile image
Francesco Di Donato

Me too! Thank you!