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:

Discussion (17)

Collapse
jonrandy profile image
Jon Randy • Edited on

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 Author • Edited on

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 on

It works 👍 And it's safe

Thread Thread
didof profile image
Francesco Di Donato Author

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 Author

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 Author

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
lukeshiru profile image
LUKESHIRU • Edited on

Love this kind of non practical but interesting kind of posts! Thanks for sharing!

Collapse
didof profile image
Francesco Di Donato Author

Me too! Thank you!