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:

Latest comments (16)

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!

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
 
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!