New Proposal: Number.range (Yes, range again)

# Jack Works (5 years ago)

Hello everyone. I've searched for the mailing list, there are some discussions add new Range or a ... b into the language but not become a formal proposal.

I've made a full demo(polyfill) these days. Anyone interested?

Preview: for (let i of Number.range(1, 5)) console.log(i)

And there is an extended version that let range support number range check if ( x in Number.range(0, 100) ). About usage, test case, and demo of the extended version is posted on gist.

gist.github.com/Jack-Works/aa8d4c5dbd0e8cdd415783de8d922451

( This proposal can also/or add an new operator instead. Number.range(x, y) => x...y Number.range(x, y, z) => (x...y).step(z) or x...z->y or something

else, and other behavior keep same with the Number.range implmentation in the gist )

Here is the base version of the proposal

Number.range = function*(from, to, step) {
    if (
        typeof from !== 'number' &&
        typeof from !== 'bigint' &&
        (typeof to !== 'number' && typeof to !== 'bigint') &&
        (typeof step !== 'number' && typeof step !== 'bigint' && typeof
step !== 'undefined')
    )
        throw new TypeError('All parameters must be a number or a BigInt')

    if (typeof from === 'bigint' && typeof step === 'undefined') step = 1n
    else if (typeof from === 'number' && typeof step === 'undefined') step
= 1

    if (typeof from !== typeof to || typeof from !== typeof step) throw new
TypeError('Type of all parameters must be the same')
    if (typeof from === "number" && Number.isNaN(from) || Number.isNaN(to)
|| Number.isNaN(step)) return;
    // Quit early with no value yield

    // Math.abs does not support BigInt.
    const abs = x => (x >= (typeof x === 'bigint' ? 0n : 0) ? x : -x)

    const increase = to > from
    // Ignore the symbol
    if (increase) step = abs(step)
    else step = -abs(step)

    let count = typeof from === 'bigint' ? 1n : 1
    let now = from
    while (increase ? !(now >= to) : !(to >= now)) {
        yield now
        now = from + step * count
        count++
    }
}
# Tab Atkins Jr. (5 years ago)

I end up writing a range function in virtually every project I use, so yeah, I think this is worthwhile.

My own preferences, based on Python precedence:

Number.range(end) === Number.range(0, end, 1) Number.range(start, end) === Number.range(start, end, Math.sign(end-start)) Number.range(start, end, step) => { if start < end && step > 0: yield all (start + k*step), for k from 0

to infinity, less than end elif start > end && step < 0: yield all (start + k*step), for k from

0 to infinity, greater than end else: yield nothing }

So:

  • [...Number.range(5)] evaluates to [0, 1, 2, 3, 4]
  • [...Number.range(0)] evaluates to []
  • [...Number.range(-5)] evaluates to []
  • [...Number.range(1, -3)] evaluates to [1, 0, -1, -2]
  • [...Number.range(-3, 1)] evaluates to [-3, -2, -1, 0]
  • [...Number.range(1, 1)] evaluates to []
  • [...Number.range(0, 10, 5)] evaluates to [0, 5]
  • [...Number.range(0, 10, -5)] evaluates to []
# Jacob Pratt (5 years ago)

Quick, simple TypeScript range function (with overloads, of course).

export function range(end: number): IterableIterator<number>;

export function range(start: number, end: number): IterableIterator<number>;

export function range(start: number, end: number, step: number):
IterableIterator<number>;

export function* range(start: number, end?: number, step?: number):
IterableIterator<number> {
  // overload #1
  if (end === undefined) {
    [start, end, step] = [0, start, 1];
  }

  // overload #2
  if (step === undefined) {
    step = Math.sign(end - start);
  }

  // ensure we have the appropriate types
  if (typeof start !== 'number' || typeof end !== 'number' || typeof step
!== 'number') {
    throw new TypeError('all parameters must be of type number');
  }

  while ((start < end && step > 0) || (start > end && step < 0)) {
    yield start;
    start += step;
  }
}

IMO, we should focus on building up a JavaScript standard library that has tons of useful utilities like this, rather than continue to add methods onto namespaces. Perhaps that's just me, though.

Jacob Pratt

# Jack Works (5 years ago)

Oh, I'm not here to find out how to implement a range in detail. That's the trivial things, I want to first make it become a stage 0 proposal, then we can discuss if we need some overload or how to deal with the edge cases. Ps, does Pratt mean we should add it as a standard library in the stage 1 standard libraray proposal?

# Jacob Pratt (5 years ago)

I'm not saying we should add it on to that proposal, but rather saying that I think more effort should be put into a standard library where things like this don't need to go through a full standards process. That's just my opinion, and borderline on-topic for this specific suggestion.

I'm absolutely in favor of having a range method, it's just where it should be that I differ.

Jacob Pratt

# Jordan Harband (5 years ago)

It's not a "standard library" unless every part of it has gone through a full standards process.

# Cyril Auburtin (5 years ago)

cf tc39/proposal-slice-notation#19 which would be more powerful as a literal

# Jack Works (5 years ago)

Hello everyone, I made a formal proposal Jack-Works/proposal-Number.range , welcome to disscuss the details in the repo!

# Cyril Auburtin (5 years ago)

I'm really cheering for the slice-notation range extension proposed in tc39/proposal-slice-notation#19

for (const i of Number.range(0, 43)) {
    console.log(i) // 0 to 42
}

const fakeData = [...Number.range(1, 21)].map(x => x ** 2)

becomes

for (const i of (0:43)) {
    console.log(i) // 0 to 42
}

const fakeData = [...(1:21)].map(x => x ** 2)
# Ed Saleh (5 years ago)

Fully Support