UNPKG

python-range

Version:

A JavaScript implementation of the Python's range() function.

194 lines (180 loc) 6.18 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.PythonRange = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; exports.default = range; var _arrayIndicesProxy = require('./array-indices-proxy'); var _arrayIndicesProxy2 = _interopRequireDefault(_arrayIndicesProxy); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const mandatory = parameter => { throw new Error(`The ${parameter} parameter is mandatory`); }; class PythonRange { constructor(...args) { if (args.length < 1) { throw new Error(`Expected at least 1 argument, got ${args.length}`); } if (args.length > 3) { throw new Error(`Expected at most 3 arguments, got ${args.length}`); } if (!args.every(Number.isInteger)) { throw new Error('All arguments must be integers'); } const step = args[2] !== undefined ? args[2] : 1; if (step === 0) { throw new Error('The step argument must not be zero'); } let [start, stop] = args; [start, stop] = stop === undefined ? [0, start] : [start, stop]; const baseDescriptor = { configurable: false, enumerable: false, writable: true }; Reflect.defineProperty(this, 'start', _extends({}, baseDescriptor, { value: start })); Reflect.defineProperty(this, 'stop', _extends({}, baseDescriptor, { value: stop })); Reflect.defineProperty(this, 'step', _extends({}, baseDescriptor, { value: step })); Reflect.defineProperty(this, 'length', { configurable: false, enumerable: false, get() { const length = Math.ceil((this.stop - this.start) / this.step); return Math.max(0, length); } }); if (typeof Proxy !== 'undefined') { const indicesProxy = new _arrayIndicesProxy2.default(this, { get(target, index) { return target.get(index); }, has(target, index) { return index < target.length; }, getOwnPropertyDescriptor(target, index) { const descriptor = { value: indicesProxy[index], configurable: false, enumerable: true, writable: false }; // It is neccessary to define this property on target, because proxy cannot // report a non-existing property as non-configurable. // See http://stackoverflow.com/q/40921884/3853934 Reflect.defineProperty(target, String(index), descriptor); return descriptor; }, defineProperty() { return false; }, set() { return false; }, deleteProperty() { return false; }, // In order to be able to create numeric properties on-demand, // the object has to be extensible. preventExtensions() { return false; } }); return indicesProxy; } return this; } get(index, ...rest) { if (rest.length !== 0) { throw new Error(`Expected one argument; got ${rest.length + 1}`); } if (!Number.isInteger(index)) { throw new Error('The index argument must be an integer'); } if (index < this.length) { return this.start + this.step * index; } return undefined; } forEach(callback = mandatory('callback'), thisArg, ...rest) { if (rest.length !== 0) { throw new Error(`Expected at most two arguments; got ${rest.length + 2}`); } for (let i = 0; i < this.length; i += 1) { callback.call(thisArg, this.get(i), i, this); } } includes(value = mandatory('value'), ...rest) { if (rest.length !== 0) { throw new Error(`Expected one argument; got ${rest.length + 1}`); } if (!Number.isInteger(value)) { throw new Error('The value argument must be an integer'); } return (this.step > 0 ? value >= this.start && value < this.stop : value > this.stop && value <= this.start) && (value - this.start) % this.step === 0; } min(...rest) { if (rest.length !== 0) { throw new Error(`Expected zero arguments; got ${rest.length}`); } if (this.length !== 0) { return this.get(this.step > 0 ? 0 : this.length - 1); } return Infinity; } max(...rest) { if (rest.length !== 0) { throw new Error(`Expected zero arguments; got ${rest.length}`); } if (this.length !== 0) { return this.get(this.step > 0 ? this.length - 1 : 0); } return -Infinity; } reverse(...rest) { if (rest.length !== 0) { throw new Error(`Expected zero arguments; got ${rest.length}`); } [this.start, this.stop, this.step] = [this.get(this.length - 1), this.start - Math.sign(this.step), -this.step]; return this; } toString() { return `range(${this.start}, ${this.stop}, ${this.step})`; } valueOf() { return this.toString(); } inspect() { return this.toString(); } *[Symbol.iterator]() { for (let i = 0; i < this.length; i += 1) { yield this.get(i); } } static areEqual(a = mandatory('a'), b = mandatory('b'), ...rest) { if (rest.length !== 0) { throw new Error(`Expected two arguments; got ${rest.length + 2}`); } if (![a, b].every(x => x instanceof PythonRange)) { throw new Error('Both arguments must be instances of PythonRange'); } // Based on https://github.com/python/cpython/blob/cff677abe1823900e954592035a170eb67840971/Objects/rangeobject.c#L425 if (a === b) return true; if (a.length !== b.length) return false; if (a.length === 0) return true; if (a.start !== b.start) return false; if (a.length === 1) return true; return a.step === b.step; } } exports.PythonRange = PythonRange; Reflect.defineProperty(PythonRange.prototype, Symbol.toStringTag, { configurable: false, writable: false, enumerable: false, value: 'PythonRange' }); function range(...args) { return new PythonRange(...args); }