range-pie 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/package.json +30 -0
- package/src/index.js +5 -0
- package/src/py-range.js +376 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 SkorpionG2000
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# range-pie
|
|
2
|
+
|
|
3
|
+
A JavaScript library that brings Python's range functionality to JavaScript, enhanced with familiar array methods. This lightweight utility provides a seamless way to work with numeric sequences while maintaining JavaScript's functional programming paradigm.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Basic Usage](#basic-usage)
|
|
9
|
+
- [API Reference](#api-reference)
|
|
10
|
+
|
|
11
|
+
- [Constructor Options](#constructor-options)
|
|
12
|
+
- [Properties](#properties)
|
|
13
|
+
- [at()](#at)
|
|
14
|
+
- [toString()](#tostring)
|
|
15
|
+
- [toArray()](#toarray)
|
|
16
|
+
- [map()](#map)
|
|
17
|
+
- [filter()](#filter)
|
|
18
|
+
- [reduce()](#reduce)
|
|
19
|
+
- [some()](#some)
|
|
20
|
+
- [every()](#every)
|
|
21
|
+
- [find()](#find)
|
|
22
|
+
- [findIndex()](#findindex)
|
|
23
|
+
- [findLastIndex()](#findlastindex)
|
|
24
|
+
- [forEach()](#foreach)
|
|
25
|
+
- [includes()](#includes)
|
|
26
|
+
- [indexOf()](#indexof)
|
|
27
|
+
- [lastIndexOf()](#lastindexof)
|
|
28
|
+
- [reverse()](#reverse)
|
|
29
|
+
|
|
30
|
+
- [Advanced Usage](#advanced-usage)
|
|
31
|
+
|
|
32
|
+
- [Iteration](#iteration)
|
|
33
|
+
- [Proxy Access](#proxy-access)
|
|
34
|
+
|
|
35
|
+
- [Examples](#examples)
|
|
36
|
+
|
|
37
|
+
- [Methods chaining](#methods-chaining)
|
|
38
|
+
- [Using as Array-like Object](#using-as-array-like-object)
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install python-range
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Basic Usage
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const { PyRange } = require('range-pie');
|
|
50
|
+
|
|
51
|
+
// Create a range from 0 to 5
|
|
52
|
+
const range = new PyRange(5);
|
|
53
|
+
console.log([...range]); // [0, 1, 2, 3, 4]
|
|
54
|
+
|
|
55
|
+
// Create a range with start and stop
|
|
56
|
+
const range2 = new PyRange(2, 8);
|
|
57
|
+
console.log([...range2]); // [2, 3, 4, 5, 6, 7]
|
|
58
|
+
|
|
59
|
+
// Create a range with step
|
|
60
|
+
const range3 = new PyRange(0, 10, 2);
|
|
61
|
+
console.log([...range3]); // [0, 2, 4, 6, 8]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API Reference
|
|
65
|
+
|
|
66
|
+
### Constructor Options
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
new PyRange(stop) // 0 to stop-1
|
|
70
|
+
new PyRange(start, stop) // start to stop-1
|
|
71
|
+
new PyRange(start, stop, step) // start to stop-1 with step
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **PyRange(stop:number)**
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
console.log(PyRange(10));
|
|
78
|
+
// PyRange { _start: 0, _stop: 10, _step: 1, _length: 10 }
|
|
79
|
+
|
|
80
|
+
console.log(PyRange(-5));
|
|
81
|
+
// PyRange { _start: 0, _stop: -5, _step: -1, _length: 5 }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- **PyRange(start:number, stop:number)**
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
console.log(PyRange(1, 10));
|
|
88
|
+
// PyRange { _start: 1, _stop: 10, _step: 1, _length: 9 }
|
|
89
|
+
|
|
90
|
+
console.log(PyRange(-10, 0));
|
|
91
|
+
// PyRange { _start: -10, _stop: 0, _step: 1, _length: 10 }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- **PyRange(start:number, stop:number, step:number)**
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
console.log(PyRange(2, 5, 2));
|
|
98
|
+
// PyRange { _start: 2, _stop: 5, _step: 2, _length: 2 }
|
|
99
|
+
|
|
100
|
+
console.log(PyRange(2, -10, -1));
|
|
101
|
+
// PyRange { _start: 2, _stop: -10, _step: -1, _length: 12 }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Properties
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
const range = new PyRange(1, 10, 2);
|
|
108
|
+
console.log(range.start); // 1
|
|
109
|
+
console.log(range.stop); // 10
|
|
110
|
+
console.log(range.step); // 2
|
|
111
|
+
console.log(range.length); // 5
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### at()
|
|
115
|
+
|
|
116
|
+
The 'at' method accepts a number as argument to gets the value at the specified index in a range. Generate a RangeError if the index is out of range.
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
120
|
+
console.log(range.at(0)); // 1
|
|
121
|
+
console.log(range.at(2)); // 3
|
|
122
|
+
console.log(range.at(-1)); // RangeError
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### toString()
|
|
126
|
+
|
|
127
|
+
Transform a PyRange object to a string representation.
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const range = new PyRange(1, 10, 2);
|
|
131
|
+
console.log(range.toString()); // "PyRange(1, 10, 2)"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### toArray()
|
|
135
|
+
|
|
136
|
+
Transform a PyRange object to an JavaScript array
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
const range = new PyRange(1, 4);
|
|
140
|
+
console.log(range.toArray()); // [1, 2, 3]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### map()
|
|
144
|
+
|
|
145
|
+
It works the same as [**`Array.prototype.map`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
const range = new PyRange(1, 4); // [1, 2, 3]
|
|
149
|
+
console.log(range.map(x => x * 2)); // [2, 4, 6]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### filter()
|
|
153
|
+
|
|
154
|
+
It works the same as [**`Array.prototype.filter`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
const range = new PyRange(1, 6); // [1, 2, 3, 4, 5]
|
|
158
|
+
console.log(range.filter(x => x % 2 === 0)); // [2, 4]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### reduce()
|
|
162
|
+
|
|
163
|
+
It works the same as [**`Array.prototype.reduce`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const range = new PyRange(1, 4); // [1, 2, 3]
|
|
167
|
+
const sum = range.reduce((acc, curr) => acc + curr, 0);
|
|
168
|
+
console.log(sum); // 6
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### some()
|
|
172
|
+
|
|
173
|
+
It works the same as [**`Array.prototype.some`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
177
|
+
console.log(range.some(x => x > 3)); // true
|
|
178
|
+
console.log(range.some(x => x < 0)); // false
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### every()
|
|
182
|
+
|
|
183
|
+
It works the same as [**`Array.prototype.every`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
187
|
+
console.log(range.every(x => x > 0)); // true
|
|
188
|
+
console.log(range.every(x => x > 2)); // false
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### find()
|
|
192
|
+
|
|
193
|
+
It works the same as [**`Array.prototype.find`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
197
|
+
console.log(range.find(x => x > 2)); // 3
|
|
198
|
+
console.log(range.find(x => x > 5)); // undefined
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### findIndex()
|
|
202
|
+
|
|
203
|
+
It works the same as [**`Array.prototype.findIndex`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
207
|
+
console.log(range.findIndex(x => x > 2)); // 2
|
|
208
|
+
console.log(range.findIndex(x => x > 5)); // -1
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### findLastIndex()
|
|
212
|
+
|
|
213
|
+
It works the same as [**`Array.prototype.findLastIndex`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex)
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
217
|
+
console.log(range.findLastIndex(x => x > 2)); // 3
|
|
218
|
+
console.log(range.findLastIndex(x => x > 5)); // -1
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### forEach()
|
|
222
|
+
|
|
223
|
+
It works the same as [**`Array.prototype.forEach`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
const range = new PyRange(1, 4); // [1, 2, 3]
|
|
227
|
+
range.forEach(x => console.log(x));
|
|
228
|
+
// 1
|
|
229
|
+
// 2
|
|
230
|
+
// 3
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### includes()
|
|
234
|
+
|
|
235
|
+
It works the same as [**`Array.prototype.includes`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
239
|
+
console.log(range.includes(3)); // true
|
|
240
|
+
console.log(range.includes(5)); // false
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### indexOf()
|
|
244
|
+
|
|
245
|
+
It works the same as [**`Array.prototype.indexOf`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
249
|
+
console.log(range.indexOf(3)); // 2
|
|
250
|
+
console.log(range.indexOf(5)); // -1
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### lastIndexOf()
|
|
254
|
+
|
|
255
|
+
It works the same as [**`Array.prototype.lastIndexOf`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf)
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
const range = new PyRange(1, 5, 1); // [1, 2, 3, 4]
|
|
259
|
+
console.log(range.lastIndexOf(3)); // 2
|
|
260
|
+
console.log(range.lastIndexOf(5)); // -1
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### reverse()
|
|
264
|
+
|
|
265
|
+
It works the same as [**`Array.prototype.reverse`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse)
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
const range = new PyRange(1, 5); // [1, 2, 3, 4]
|
|
269
|
+
console.log([...range]); // [1, 2, 3, 4]
|
|
270
|
+
|
|
271
|
+
const reversed = range.reverse(); // [4, 3, 2, 1]
|
|
272
|
+
console.log([...reversed]); // [4, 3, 2, 1]
|
|
273
|
+
|
|
274
|
+
const rangeWithStep = new PyRange(1, 10, 2); // [1, 3, 5, 7, 9]
|
|
275
|
+
const reversedStep = rangeWithStep.reverse(); // [9, 7, 5, 3, 1]
|
|
276
|
+
console.log([...reversedStep]);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Advanced Usage
|
|
280
|
+
|
|
281
|
+
### Iteration
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
const range = new PyRange(3);
|
|
285
|
+
|
|
286
|
+
// For...of loop
|
|
287
|
+
for (const num of range) {
|
|
288
|
+
console.log(num); // 0, 1, 2
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Spread operator
|
|
292
|
+
const array = [...range]; // [0, 1, 2]
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Proxy Access
|
|
296
|
+
|
|
297
|
+
Proxy access allows access to the array elements using bracket notation, just like a regular array.
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
const range = new PyRange(5);
|
|
301
|
+
const proxy = range.asProxy();
|
|
302
|
+
|
|
303
|
+
console.log(proxy[0]); // 0
|
|
304
|
+
console.log(proxy[3]); // 3
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Examples
|
|
308
|
+
|
|
309
|
+
### Methods chaining
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
const range = new PyRange(1, 6);
|
|
313
|
+
const squares = range
|
|
314
|
+
.filter(x => x % 2 === 0)
|
|
315
|
+
.map(x => x * x);
|
|
316
|
+
console.log(squares); // [4, 16]
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Using as Array-like Object
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
const range = new PyRange(5).asProxy();
|
|
323
|
+
const firstThree = [range[0], range[1], range[2]];
|
|
324
|
+
console.log(firstThree); // [0, 1, 2]
|
|
325
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "range-pie",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A JavaScript class that simulate Python's range function, combined with several useful JavaScript array methods.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./src/index.js",
|
|
9
|
+
"require": "./src/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"range",
|
|
17
|
+
"Python range",
|
|
18
|
+
"Python",
|
|
19
|
+
"JavaScript",
|
|
20
|
+
"JavaScript Array",
|
|
21
|
+
"Array",
|
|
22
|
+
"py-range",
|
|
23
|
+
"PyRange"
|
|
24
|
+
],
|
|
25
|
+
"author": "SkorpionG2000",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"jest": "^29.7.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.js
ADDED
package/src/py-range.js
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class PyRange {
|
|
4
|
+
/**
|
|
5
|
+
* A class that simulate Python's range function, combined with several useful JavaScript array methods.
|
|
6
|
+
* @param {...number} args - The arguments of the range. The possible forms are
|
|
7
|
+
* - `PyRange(stop)`
|
|
8
|
+
* - `PyRange(start, stop)`
|
|
9
|
+
* - `PyRange(start, stop, step)`
|
|
10
|
+
* @throws {TypeError} If any of the arguments is not a number.
|
|
11
|
+
* @throws {TypeError} If any of the arguments is not an integer.
|
|
12
|
+
* @throws {Error} If the step is zero.
|
|
13
|
+
* @throws {Error} If the arguments count is not between 1 and 3.
|
|
14
|
+
* @property {number} start - The start of the range, inclusive.
|
|
15
|
+
* @property {number} stop - The stop of the range, exclusive.
|
|
16
|
+
* @property {number} step - The step of the range.
|
|
17
|
+
* @property {number} length - The length of the range.
|
|
18
|
+
*/
|
|
19
|
+
constructor(...args) {
|
|
20
|
+
if (!args.every((arg) => typeof arg === "number")) {
|
|
21
|
+
throw new TypeError("All arguments must be numbers");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!args.every(Number.isInteger)) {
|
|
25
|
+
throw new TypeError("All arguments must be integers");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const [start, stop, step] =
|
|
29
|
+
args.length === 1
|
|
30
|
+
? [0, args[0], 1]
|
|
31
|
+
: args.length === 2
|
|
32
|
+
? [args[0], args[1], 1]
|
|
33
|
+
: args.length === 3
|
|
34
|
+
? [args[0], args[1], args[2]]
|
|
35
|
+
: (() => {
|
|
36
|
+
throw new Error("Invalid arguments count: must between 1 and 3");
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
if (step === 0) {
|
|
40
|
+
throw new Error("Step cannot be zero");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this._start = start;
|
|
44
|
+
this._stop = stop;
|
|
45
|
+
this._step = stop < start && args.length !== 3 ? -step : step;
|
|
46
|
+
|
|
47
|
+
const diff = this._step > 0 ? stop - start : start - stop;
|
|
48
|
+
this._length = Math.max(Math.ceil(diff / Math.abs(this._step)), 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Gets the length of this range.
|
|
53
|
+
*
|
|
54
|
+
* @returns {number} the length of this range
|
|
55
|
+
* @readonly
|
|
56
|
+
*/
|
|
57
|
+
get length() {
|
|
58
|
+
return this._length;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Gets the starting value of the range.
|
|
63
|
+
*
|
|
64
|
+
* @returns {number} The starting value of the range.
|
|
65
|
+
* @readonly
|
|
66
|
+
*/
|
|
67
|
+
get start() {
|
|
68
|
+
return this._start;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets the ending value of the range.
|
|
73
|
+
*
|
|
74
|
+
* @returns {number} The ending value of the range.
|
|
75
|
+
* @readonly
|
|
76
|
+
*/
|
|
77
|
+
get stop() {
|
|
78
|
+
return this._stop;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Gets the step value of the range.
|
|
83
|
+
*
|
|
84
|
+
* @returns {number} The step value of the range.
|
|
85
|
+
* @readonly
|
|
86
|
+
*/
|
|
87
|
+
get step() {
|
|
88
|
+
return this._step;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Gets the value at the specified index in this range.
|
|
93
|
+
* @param {number} index - The index of the value to retrieve
|
|
94
|
+
* @returns {number} The value at the specified index
|
|
95
|
+
* @throws {RangeError} If the index is out of range
|
|
96
|
+
*/
|
|
97
|
+
at(index) {
|
|
98
|
+
if (index < 0 || index >= this._length) {
|
|
99
|
+
throw new RangeError("Index out of range");
|
|
100
|
+
}
|
|
101
|
+
return this._start + index * this._step;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Converts the range to a string.
|
|
106
|
+
* @returns {string} A string of the form `Range(start, stop, step)`.
|
|
107
|
+
*/
|
|
108
|
+
toString() {
|
|
109
|
+
return `PyRange(${this._start}, ${this._stop}, ${this._step})`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Converts the range to an array.
|
|
114
|
+
* @returns {number[]} An array of numbers with the same elements as this range.
|
|
115
|
+
*/
|
|
116
|
+
toArray() {
|
|
117
|
+
return [...this];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static #validateCb(cb) {
|
|
121
|
+
if (typeof cb !== "function") {
|
|
122
|
+
throw new TypeError("Callback must be a function");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates a new array with the results of applying the given callback
|
|
128
|
+
* function to every element in this range.
|
|
129
|
+
* @param {function(number, number, PyRange): *} callback - The callback
|
|
130
|
+
* function to apply to every element.
|
|
131
|
+
* @returns {Array.<*>} A new array of the same length as this range.
|
|
132
|
+
*/
|
|
133
|
+
map(callback) {
|
|
134
|
+
PyRange.#validateCb(callback);
|
|
135
|
+
|
|
136
|
+
const result = new Array(this._length);
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < this._length; i++) {
|
|
139
|
+
result[i] = callback(this.at(i), i, this);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Creates a new array with all elements that pass the test implemented by
|
|
147
|
+
* the provided function.
|
|
148
|
+
* @param {function(number, number, PyRange): boolean} callback - The
|
|
149
|
+
* predicate function to apply to every element
|
|
150
|
+
* @returns {number[]} A new array of elements that pass the test
|
|
151
|
+
*/
|
|
152
|
+
filter(callback) {
|
|
153
|
+
PyRange.#validateCb(callback);
|
|
154
|
+
|
|
155
|
+
const result = [];
|
|
156
|
+
|
|
157
|
+
for (let i = 0; i < this._length; i++) {
|
|
158
|
+
if (callback(this.at(i), i, this)) {
|
|
159
|
+
result.push(this.at(i));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Reduces the range to a single value.
|
|
168
|
+
* @param {function(*, number, number, PyRange): *} callback - The callback
|
|
169
|
+
* function to apply to every element. The callback should take four
|
|
170
|
+
* arguments: the accumulator, the current value, the index of the current
|
|
171
|
+
* value, and the range object.
|
|
172
|
+
* @param {*} [initialValue] - The initial value of the accumulator.
|
|
173
|
+
* @returns {*} The final value of the accumulator.
|
|
174
|
+
*/
|
|
175
|
+
reduce(callback, initialValue) {
|
|
176
|
+
PyRange.#validateCb(callback);
|
|
177
|
+
|
|
178
|
+
let accumulator = initialValue;
|
|
179
|
+
|
|
180
|
+
for (let i = 0; i < this._length; i++) {
|
|
181
|
+
accumulator = callback(accumulator, this.at(i), i, this);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return accumulator;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Determines whether at least one element of the range satisfies the
|
|
189
|
+
* provided test.
|
|
190
|
+
* @param {function(number, number, PyRange): boolean} callback - The
|
|
191
|
+
* predicate function to apply to every element
|
|
192
|
+
* @returns {boolean} True if at least one element of the range passes the
|
|
193
|
+
* test, false otherwise.
|
|
194
|
+
*/
|
|
195
|
+
some(callback) {
|
|
196
|
+
PyRange.#validateCb(callback);
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i < this._length; i++) {
|
|
199
|
+
if (callback(this.at(i), i, this)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Determines whether all elements of the range satisfy the provided test.
|
|
208
|
+
* @param {function(number, number, PyRange): boolean} callback - The
|
|
209
|
+
* predicate function to apply to every element
|
|
210
|
+
* @returns {boolean} True if all elements of the range pass the test,
|
|
211
|
+
* false otherwise.
|
|
212
|
+
*/
|
|
213
|
+
every(callback) {
|
|
214
|
+
PyRange.#validateCb(callback);
|
|
215
|
+
|
|
216
|
+
for (let i = 0; i < this._length; i++) {
|
|
217
|
+
if (!callback(this.at(i), i, this)) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Finds the first element in this range that satisfies the provided test.
|
|
226
|
+
* @param {function(number, number, PyRange): boolean} callback - The
|
|
227
|
+
* predicate function to apply to every element
|
|
228
|
+
* @returns {number|undefined} The first element that passes the test,
|
|
229
|
+
* or undefined if no element passes the test.
|
|
230
|
+
*/
|
|
231
|
+
find(callback) {
|
|
232
|
+
PyRange.#validateCb(callback);
|
|
233
|
+
|
|
234
|
+
for (let i = 0; i < this._length; i++) {
|
|
235
|
+
if (callback(this.at(i), i, this)) {
|
|
236
|
+
return this.at(i);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Finds the index of the first element in this range that satisfies the provided test.
|
|
244
|
+
* @param {function(number, number, PyRange): boolean} callback - The predicate function to apply to each element.
|
|
245
|
+
* @returns {number} The index of the first element that passes the test, or -1 if no element passes the test.
|
|
246
|
+
*/
|
|
247
|
+
findIndex(callback) {
|
|
248
|
+
PyRange.#validateCb(callback);
|
|
249
|
+
|
|
250
|
+
for (let i = 0; i < this._length; i++) {
|
|
251
|
+
if (callback(this.at(i), i, this)) {
|
|
252
|
+
return i;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return -1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Finds the index of the last element in this range that satisfies the provided test.
|
|
260
|
+
* @param {function(number, number, PyRange): boolean} callback - The predicate function to apply to each element.
|
|
261
|
+
* @returns {number} The index of the last element that passes the test, or -1 if no element passes the test.
|
|
262
|
+
*/
|
|
263
|
+
findLastIndex(callback) {
|
|
264
|
+
PyRange.#validateCb(callback);
|
|
265
|
+
|
|
266
|
+
for (let i = this._length - 1; i >= 0; i--) {
|
|
267
|
+
if (callback(this.at(i), i, this)) {
|
|
268
|
+
return i;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return -1;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Executes a provided function once for each element in this range.
|
|
276
|
+
* @param {function(number, number, PyRange): void} callback - The
|
|
277
|
+
* function to execute for each element.
|
|
278
|
+
*/
|
|
279
|
+
forEach(callback) {
|
|
280
|
+
PyRange.#validateCb(callback);
|
|
281
|
+
|
|
282
|
+
for (let i = 0; i < this._length; i++) {
|
|
283
|
+
callback(this.at(i), i, this);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Determines whether the given value is present in this range.
|
|
289
|
+
* @param {*} value - The value to search for.
|
|
290
|
+
* @returns {boolean} True if the value is present, false otherwise.
|
|
291
|
+
*/
|
|
292
|
+
includes(value) {
|
|
293
|
+
return this.some((item) => item === value);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Returns the index of the first occurrence of the specified value, or -1 if it is not present.
|
|
298
|
+
* @param {*} value - The value to search for.
|
|
299
|
+
* @returns {number} The index of the value, or -1 if it is not present.
|
|
300
|
+
*/
|
|
301
|
+
indexOf(value) {
|
|
302
|
+
for (let i = 0; i < this._length; i++) {
|
|
303
|
+
if (this.at(i) === value) {
|
|
304
|
+
return i;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return -1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Returns the index of the last occurrence of the specified value, or -1 if it is not present.
|
|
312
|
+
* @param {*} value - The value to search for.
|
|
313
|
+
* @returns {number} The index of the last occurrence of the value, or -1 if it is not present.
|
|
314
|
+
*/
|
|
315
|
+
lastIndexOf(value) {
|
|
316
|
+
for (let i = this._length - 1; i >= 0; i--) {
|
|
317
|
+
if (this.at(i) === value) {
|
|
318
|
+
return i;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return -1;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Reverses the order of the elements in this range, returning a new PyRange object.
|
|
326
|
+
* @returns {PyRange} A new PyRange object with the elements in reverse order.
|
|
327
|
+
*/
|
|
328
|
+
reverse() {
|
|
329
|
+
const result = new PyRange(this._stop, this._start, -this._step);
|
|
330
|
+
result._length = this._length;
|
|
331
|
+
return result;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
[Symbol.iterator]() {
|
|
335
|
+
let index = 0;
|
|
336
|
+
let current = this._start;
|
|
337
|
+
const { _stop: _, _step: step, _length: length } = this;
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
next: () => {
|
|
341
|
+
if (index >= length) {
|
|
342
|
+
return { done: true };
|
|
343
|
+
} else {
|
|
344
|
+
const value = current;
|
|
345
|
+
current += step;
|
|
346
|
+
index++;
|
|
347
|
+
return { value, done: false };
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Returns a Proxy for this range, allowing indexed access.
|
|
355
|
+
* This proxy enables accessing range elements via array-like indexing.
|
|
356
|
+
* If the property is a number, it will return the element at that index.
|
|
357
|
+
*
|
|
358
|
+
* @returns {Proxy} A proxy for the PyRange instance.
|
|
359
|
+
*/
|
|
360
|
+
asProxy() {
|
|
361
|
+
return new Proxy(this, {
|
|
362
|
+
get(target, prop) {
|
|
363
|
+
if (typeof prop === "symbol") {
|
|
364
|
+
return target[prop];
|
|
365
|
+
}
|
|
366
|
+
if (!isNaN(prop)) {
|
|
367
|
+
return target.at(parseInt(prop));
|
|
368
|
+
}
|
|
369
|
+
return target[prop];
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
module.exports = PyRange;
|
|
376
|
+
module.exports.PyRange = PyRange;
|