extra-iterator 0.11.0 → 0.13.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/dist/index.d.ts +63 -13
- package/dist/index.js +88 -30
- package/dist/index.test.js +114 -12
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,33 @@ interface ArrayIsh<T> {
|
|
|
3
3
|
length: number;
|
|
4
4
|
}
|
|
5
5
|
export type ExtraIteratorSource<T> = Iterator<T, any, any> | Iterable<T, any, any> | ArrayIsh<T>;
|
|
6
|
+
/**
|
|
7
|
+
* An extended iterator class that provides additional chainable utility methods for working with iterables.
|
|
8
|
+
*
|
|
9
|
+
* @template T The type of values yielded by this iterator.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Creating an iterator from an array
|
|
13
|
+
* const iter = ExtraIterator.from([1, 2, 3, 4, 5]);
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Creating a sequence of ancestors of a given element
|
|
17
|
+
* ExtraIterator.from(function*() {
|
|
18
|
+
*
|
|
19
|
+
* }()).toArray();
|
|
20
|
+
* ExtraIterator.from([1, 2, 3, 4, 5])
|
|
21
|
+
* .filter(n => n % 2 === 0)
|
|
22
|
+
* .map(n => n * 2)
|
|
23
|
+
* .toArray()
|
|
24
|
+
* // returns [4, 8]
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Using static factory methods
|
|
28
|
+
* ExtraIterator.range(1, 5)
|
|
29
|
+
* .map(n => n * n)
|
|
30
|
+
* .toArray()
|
|
31
|
+
* // returns [1, 4, 9, 16]
|
|
32
|
+
*/
|
|
6
33
|
export declare class ExtraIterator<T> extends Iterator<T, any, any> {
|
|
7
34
|
static from<T>(source: ExtraIteratorSource<T>): ExtraIterator<T>;
|
|
8
35
|
static zip<A, B>(a: ExtraIteratorSource<A>, b: ExtraIteratorSource<B>): ExtraIterator<[A, B]>;
|
|
@@ -25,6 +52,12 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
|
|
|
25
52
|
* @example ExtraIterator.empty().toArray() // returns []
|
|
26
53
|
*/
|
|
27
54
|
static empty<T = any>(): ExtraIterator<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Creates an iterator that yields a single value.
|
|
57
|
+
*
|
|
58
|
+
* @example ExtraIterator.single(42).toArray() // returns [42]
|
|
59
|
+
*/
|
|
60
|
+
static single<T>(value: T): ExtraIterator<T>;
|
|
28
61
|
/**
|
|
29
62
|
* Creates an iterator that yields incrementing numbers.
|
|
30
63
|
*
|
|
@@ -70,14 +103,30 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
|
|
|
70
103
|
*/
|
|
71
104
|
static repeat<T>(value: T): ExtraIterator<T>;
|
|
72
105
|
/**
|
|
73
|
-
* Generates an infinite sequence of
|
|
74
|
-
*
|
|
106
|
+
* Generates an infinite sequence of random numbers between 0 and 1 (exclusive) using `Math.random` or another
|
|
107
|
+
* specified random number generator.
|
|
75
108
|
*
|
|
76
109
|
* > ⚠ This iterator is infinite. Use {@link take} method if you want a specific number of values.
|
|
77
110
|
*/
|
|
78
|
-
static random(
|
|
79
|
-
|
|
80
|
-
|
|
111
|
+
static random(rng?: () => number): ExtraIterator<number>;
|
|
112
|
+
/**
|
|
113
|
+
* Generates an infinite sequence of cryptographically strong random bytes using `crypto.getRandomValues`, in
|
|
114
|
+
* chunks of `chunkSize` bytes.
|
|
115
|
+
*
|
|
116
|
+
* By default, this method reuses the same `ArrayBuffer` instance for each chunk, refilling it with new random
|
|
117
|
+
* values at each iteration, so you should not keep references to the yielded buffers. Set `reuseBuffer` to `false`
|
|
118
|
+
* if you want this method to yield copies of the buffer instead.
|
|
119
|
+
*
|
|
120
|
+
* If you want a flat sequence of individual byte values instead of chunks, you can chain the iterator returned by
|
|
121
|
+
* this method with the {@link flatten} method. The resulting iterator will contain interger values from 0 to 255
|
|
122
|
+
* (inclusive).
|
|
123
|
+
*
|
|
124
|
+
* > ⚠ This iterator is infinite. Use {@link take} method if you want a specific number of values.
|
|
125
|
+
*/
|
|
126
|
+
static randomBytes({ chunkSize, reuseBuffer }?: {
|
|
127
|
+
chunkSize?: number | undefined;
|
|
128
|
+
reuseBuffer?: boolean | undefined;
|
|
129
|
+
}): ExtraIterator<ArrayBuffer>;
|
|
81
130
|
private constructor();
|
|
82
131
|
private source;
|
|
83
132
|
next(value?: any): IteratorResult<T, any>;
|
|
@@ -163,11 +212,11 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
|
|
|
163
212
|
* @example
|
|
164
213
|
*
|
|
165
214
|
* ExtraIterator.from([4, 5, 6])
|
|
166
|
-
* .
|
|
215
|
+
* .prependAll([1, 2, 3])
|
|
167
216
|
* .toArray()
|
|
168
217
|
* // returns [1, 2, 3, 4, 5, 6]
|
|
169
218
|
*/
|
|
170
|
-
|
|
219
|
+
prependAll<U>(items: Iterable<U>): ExtraIterator<T | U>;
|
|
171
220
|
/**
|
|
172
221
|
* Creates a new iterator that invokes the provided callback function over each element of this iterator and yields
|
|
173
222
|
* the elements for which the callback returns `true`, only for as long as the callback returns `true`.
|
|
@@ -213,9 +262,9 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
|
|
|
213
262
|
/**
|
|
214
263
|
* Groups the elements in this iterator into groups of variable size.
|
|
215
264
|
*
|
|
216
|
-
* This method calls the provided predicate function for each pair of adjacent elements in this iterator.
|
|
217
|
-
* return `true` if the elements should belong to the same group, or `false` if they should belong
|
|
218
|
-
* groups.
|
|
265
|
+
* This method calls the provided predicate function for each pair of adjacent elements in this iterator. The
|
|
266
|
+
* function should return `true` if the elements should belong to the same group, or `false` if they should belong
|
|
267
|
+
* to different groups.
|
|
219
268
|
*
|
|
220
269
|
* The resulting iterator yields arrays of elements that belong to the same group.
|
|
221
270
|
*
|
|
@@ -292,7 +341,8 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
|
|
|
292
341
|
*/
|
|
293
342
|
splice(startIndex: number, deleteCount: number, ...newItems: T[]): ExtraIterator<T>;
|
|
294
343
|
/**
|
|
295
|
-
*
|
|
344
|
+
* If this iterator is empty, returns an iterator with the provided element as its only element; otherwise, it
|
|
345
|
+
* returns a copy of this iterator.
|
|
296
346
|
*/
|
|
297
347
|
defaultIfEmpty(provider: () => T): ExtraIterator<T>;
|
|
298
348
|
/**
|
|
@@ -380,8 +430,8 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
|
|
|
380
430
|
*
|
|
381
431
|
* @example
|
|
382
432
|
*
|
|
383
|
-
* ExtraIterator.from([1, 2, 3]).
|
|
384
|
-
* ExtraIterator.from([1, 2, 3, 1]).
|
|
433
|
+
* ExtraIterator.from([1, 2, 3]).testUnique() // returns true
|
|
434
|
+
* ExtraIterator.from([1, 2, 3, 1]).testUnique() // returns false
|
|
385
435
|
*/
|
|
386
436
|
testUnique(mapper?: (value: T) => unknown): boolean;
|
|
387
437
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An extended iterator class that provides additional chainable utility methods for working with iterables.
|
|
3
|
+
*
|
|
4
|
+
* @template T The type of values yielded by this iterator.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Creating an iterator from an array
|
|
8
|
+
* const iter = ExtraIterator.from([1, 2, 3, 4, 5]);
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Creating a sequence of ancestors of a given element
|
|
12
|
+
* ExtraIterator.from(function*() {
|
|
13
|
+
*
|
|
14
|
+
* }()).toArray();
|
|
15
|
+
* ExtraIterator.from([1, 2, 3, 4, 5])
|
|
16
|
+
* .filter(n => n % 2 === 0)
|
|
17
|
+
* .map(n => n * 2)
|
|
18
|
+
* .toArray()
|
|
19
|
+
* // returns [4, 8]
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Using static factory methods
|
|
23
|
+
* ExtraIterator.range(1, 5)
|
|
24
|
+
* .map(n => n * n)
|
|
25
|
+
* .toArray()
|
|
26
|
+
* // returns [1, 4, 9, 16]
|
|
27
|
+
*/
|
|
1
28
|
export class ExtraIterator extends Iterator {
|
|
2
29
|
// =================================================================================================================
|
|
3
30
|
// STATIC FUNCTIONS
|
|
4
31
|
// =================================================================================================================
|
|
5
|
-
// TODO Consider using a lib like `make-iterator` to transform things into iterators
|
|
6
32
|
static from(source) {
|
|
7
33
|
if (!(Symbol.iterator in source) && 'length' in source) {
|
|
8
34
|
return new ExtraIterator(function* () {
|
|
@@ -29,6 +55,14 @@ export class ExtraIterator extends Iterator {
|
|
|
29
55
|
static empty() {
|
|
30
56
|
return new ExtraIterator([]);
|
|
31
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates an iterator that yields a single value.
|
|
60
|
+
*
|
|
61
|
+
* @example ExtraIterator.single(42).toArray() // returns [42]
|
|
62
|
+
*/
|
|
63
|
+
static single(value) {
|
|
64
|
+
return new ExtraIterator([value]);
|
|
65
|
+
}
|
|
32
66
|
/**
|
|
33
67
|
* Creates an iterator that yields incrementing numbers.
|
|
34
68
|
*
|
|
@@ -98,16 +132,37 @@ export class ExtraIterator extends Iterator {
|
|
|
98
132
|
}());
|
|
99
133
|
}
|
|
100
134
|
/**
|
|
101
|
-
* Generates an infinite sequence of
|
|
102
|
-
*
|
|
135
|
+
* Generates an infinite sequence of random numbers between 0 and 1 (exclusive) using `Math.random` or another
|
|
136
|
+
* specified random number generator.
|
|
103
137
|
*
|
|
104
138
|
* > ⚠ This iterator is infinite. Use {@link take} method if you want a specific number of values.
|
|
105
139
|
*/
|
|
106
|
-
static random(
|
|
107
|
-
|
|
140
|
+
static random(rng = Math.random) {
|
|
141
|
+
return ExtraIterator.from(function* () {
|
|
142
|
+
while (true) {
|
|
143
|
+
yield rng();
|
|
144
|
+
}
|
|
145
|
+
}());
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Generates an infinite sequence of cryptographically strong random bytes using `crypto.getRandomValues`, in
|
|
149
|
+
* chunks of `chunkSize` bytes.
|
|
150
|
+
*
|
|
151
|
+
* By default, this method reuses the same `ArrayBuffer` instance for each chunk, refilling it with new random
|
|
152
|
+
* values at each iteration, so you should not keep references to the yielded buffers. Set `reuseBuffer` to `false`
|
|
153
|
+
* if you want this method to yield copies of the buffer instead.
|
|
154
|
+
*
|
|
155
|
+
* If you want a flat sequence of individual byte values instead of chunks, you can chain the iterator returned by
|
|
156
|
+
* this method with the {@link flatten} method. The resulting iterator will contain interger values from 0 to 255
|
|
157
|
+
* (inclusive).
|
|
158
|
+
*
|
|
159
|
+
* > ⚠ This iterator is infinite. Use {@link take} method if you want a specific number of values.
|
|
160
|
+
*/
|
|
161
|
+
static randomBytes({ chunkSize = 1024, reuseBuffer = true } = {}) {
|
|
162
|
+
const bytes = new Uint8Array(chunkSize);
|
|
108
163
|
return new ExtraIterator(function* () {
|
|
109
|
-
globalThis.crypto.getRandomValues(
|
|
110
|
-
yield
|
|
164
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
165
|
+
yield reuseBuffer ? bytes.buffer : new Uint8Array(bytes).buffer;
|
|
111
166
|
}())
|
|
112
167
|
.loop();
|
|
113
168
|
}
|
|
@@ -140,17 +195,19 @@ export class ExtraIterator extends Iterator {
|
|
|
140
195
|
take(limit) {
|
|
141
196
|
return limit >= 0
|
|
142
197
|
? ExtraIterator.from(super.take(limit))
|
|
143
|
-
:
|
|
198
|
+
: this.takeLast(-limit);
|
|
144
199
|
}
|
|
145
200
|
takeLast(count) {
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
result.shift();
|
|
151
|
-
}
|
|
201
|
+
const ringbuffer = new Array(count);
|
|
202
|
+
let index = 0;
|
|
203
|
+
for (let item; item = this.next(), !item.done; index = (index + 1) % count) {
|
|
204
|
+
ringbuffer[index] = item.value;
|
|
152
205
|
}
|
|
153
|
-
return
|
|
206
|
+
return ExtraIterator.from(function* () {
|
|
207
|
+
for (let i = index; i < count + index; i++) {
|
|
208
|
+
yield ringbuffer[i % count];
|
|
209
|
+
}
|
|
210
|
+
}());
|
|
154
211
|
}
|
|
155
212
|
drop(count) {
|
|
156
213
|
return count >= 0
|
|
@@ -174,7 +231,11 @@ export class ExtraIterator extends Iterator {
|
|
|
174
231
|
* @example ExtraIterator.from([[1, 2], [3, 4]]).flatten().toArray() // returns [1, 2, 3, 4]
|
|
175
232
|
*/
|
|
176
233
|
flatten() {
|
|
177
|
-
return this.flatMap(value =>
|
|
234
|
+
return this.flatMap(value => typeof value === 'object'
|
|
235
|
+
&& value !== null
|
|
236
|
+
&& Symbol.iterator in value
|
|
237
|
+
? new ExtraIterator(value).flatten()
|
|
238
|
+
: [value]);
|
|
178
239
|
}
|
|
179
240
|
/**
|
|
180
241
|
* Creates a new iterator that yields the values of this iterator, but won't yield any duplicates.
|
|
@@ -271,11 +332,11 @@ export class ExtraIterator extends Iterator {
|
|
|
271
332
|
* @example
|
|
272
333
|
*
|
|
273
334
|
* ExtraIterator.from([4, 5, 6])
|
|
274
|
-
* .
|
|
335
|
+
* .prependAll([1, 2, 3])
|
|
275
336
|
* .toArray()
|
|
276
337
|
* // returns [1, 2, 3, 4, 5, 6]
|
|
277
338
|
*/
|
|
278
|
-
|
|
339
|
+
prependAll(items) {
|
|
279
340
|
return ExtraIterator.from(function* () {
|
|
280
341
|
yield* items;
|
|
281
342
|
yield* this;
|
|
@@ -351,9 +412,9 @@ export class ExtraIterator extends Iterator {
|
|
|
351
412
|
/**
|
|
352
413
|
* Groups the elements in this iterator into groups of variable size.
|
|
353
414
|
*
|
|
354
|
-
* This method calls the provided predicate function for each pair of adjacent elements in this iterator.
|
|
355
|
-
* return `true` if the elements should belong to the same group, or `false` if they should belong
|
|
356
|
-
* groups.
|
|
415
|
+
* This method calls the provided predicate function for each pair of adjacent elements in this iterator. The
|
|
416
|
+
* function should return `true` if the elements should belong to the same group, or `false` if they should belong
|
|
417
|
+
* to different groups.
|
|
357
418
|
*
|
|
358
419
|
* The resulting iterator yields arrays of elements that belong to the same group.
|
|
359
420
|
*
|
|
@@ -373,7 +434,7 @@ export class ExtraIterator extends Iterator {
|
|
|
373
434
|
let chunk = [first.value];
|
|
374
435
|
for (let left = first, right, index = 0; right = this.next(), !right.done; left = right, index++) {
|
|
375
436
|
if (predicate(left.value, right.value, index, chunk)) {
|
|
376
|
-
chunk.push(
|
|
437
|
+
chunk.push(right.value);
|
|
377
438
|
}
|
|
378
439
|
else {
|
|
379
440
|
yield chunk;
|
|
@@ -489,8 +550,7 @@ export class ExtraIterator extends Iterator {
|
|
|
489
550
|
*/
|
|
490
551
|
splice(startIndex, deleteCount, ...newItems) {
|
|
491
552
|
if (startIndex < 0) {
|
|
492
|
-
return ExtraIterator.from(this.toArray()
|
|
493
|
-
.toSpliced(startIndex, deleteCount, ...newItems));
|
|
553
|
+
return ExtraIterator.from(this.toArray().toSpliced(startIndex, deleteCount, ...newItems));
|
|
494
554
|
}
|
|
495
555
|
return ExtraIterator.from(function* () {
|
|
496
556
|
for (let index = 0, next; next = this.next(), !next.done; index++) {
|
|
@@ -504,7 +564,8 @@ export class ExtraIterator extends Iterator {
|
|
|
504
564
|
}.call(this));
|
|
505
565
|
}
|
|
506
566
|
/**
|
|
507
|
-
*
|
|
567
|
+
* If this iterator is empty, returns an iterator with the provided element as its only element; otherwise, it
|
|
568
|
+
* returns a copy of this iterator.
|
|
508
569
|
*/
|
|
509
570
|
defaultIfEmpty(provider) {
|
|
510
571
|
return ExtraIterator.from(function* () {
|
|
@@ -528,9 +589,6 @@ export class ExtraIterator extends Iterator {
|
|
|
528
589
|
loop(times = Infinity) {
|
|
529
590
|
return ExtraIterator.from(function* () {
|
|
530
591
|
const values = this.toArray();
|
|
531
|
-
if (values.length === 0) {
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
592
|
for (let i = 0; i < times; i++) {
|
|
535
593
|
yield* values;
|
|
536
594
|
}
|
|
@@ -663,8 +721,8 @@ export class ExtraIterator extends Iterator {
|
|
|
663
721
|
*
|
|
664
722
|
* @example
|
|
665
723
|
*
|
|
666
|
-
* ExtraIterator.from([1, 2, 3]).
|
|
667
|
-
* ExtraIterator.from([1, 2, 3, 1]).
|
|
724
|
+
* ExtraIterator.from([1, 2, 3]).testUnique() // returns true
|
|
725
|
+
* ExtraIterator.from([1, 2, 3, 1]).testUnique() // returns false
|
|
668
726
|
*/
|
|
669
727
|
testUnique(mapper) {
|
|
670
728
|
const seen = new Set();
|
package/dist/index.test.js
CHANGED
|
@@ -7,12 +7,24 @@ describe(ExtraIterator.name, () => {
|
|
|
7
7
|
const iterator = ExtraIterator.from(array);
|
|
8
8
|
expect(iterator.toArray()).toEqual(array);
|
|
9
9
|
});
|
|
10
|
+
it('should create an ExtraIterator from an "array-like" object', () => {
|
|
11
|
+
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
|
|
12
|
+
const iterator = ExtraIterator.from(arrayLike);
|
|
13
|
+
expect(iterator.toArray()).toEqual(['a', 'b', 'c']);
|
|
14
|
+
});
|
|
10
15
|
it('should zip two iterators', () => {
|
|
11
16
|
const a = ExtraIterator.from([1, 2, 3]);
|
|
12
17
|
const b = ExtraIterator.from(['a', 'b', 'c']);
|
|
13
|
-
const zipped =
|
|
18
|
+
const zipped = a.zip(b);
|
|
14
19
|
expect(zipped.toArray()).toEqual([[1, 'a'], [2, 'b'], [3, 'c']]);
|
|
15
20
|
});
|
|
21
|
+
it('should zip three iterators', () => {
|
|
22
|
+
const a = ExtraIterator.from([1, 2, 3]);
|
|
23
|
+
const b = ExtraIterator.from(['a', 'b', 'c']);
|
|
24
|
+
const c = ExtraIterator.from([true, false, true]);
|
|
25
|
+
const zipped = ExtraIterator.zip(a, b, c);
|
|
26
|
+
expect(zipped.toArray()).toEqual([[1, 'a', true], [2, 'b', false], [3, 'c', true]]);
|
|
27
|
+
});
|
|
16
28
|
it('should count from 0 to a given number', () => {
|
|
17
29
|
const iterator = ExtraIterator.count().take(5);
|
|
18
30
|
expect(iterator.toArray()).toEqual([0, 1, 2, 3, 4]);
|
|
@@ -25,10 +37,15 @@ describe(ExtraIterator.name, () => {
|
|
|
25
37
|
const iterator = ExtraIterator.repeat('x').take(3);
|
|
26
38
|
expect(iterator.toArray()).toEqual(['x', 'x', 'x']);
|
|
27
39
|
});
|
|
28
|
-
it('should yield random
|
|
40
|
+
it('should yield random numbers', () => {
|
|
29
41
|
const values = ExtraIterator.random().take(10000).toArray();
|
|
30
42
|
expect(values.length).toBe(10000);
|
|
31
|
-
expect(values.every(value => typeof value === 'number' && value >= 0 && value <
|
|
43
|
+
expect(values.every(value => typeof value === 'number' && value >= 0 && value < 1)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
it('should yield random bytes', () => {
|
|
46
|
+
const values = ExtraIterator.randomBytes({ chunkSize: 1024 }).take(1).map(chunk => new Uint8Array(chunk)).flatten().toArray();
|
|
47
|
+
expect(values.length).toBe(1024);
|
|
48
|
+
expect(values.every(value => typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 255)).toBe(true);
|
|
32
49
|
});
|
|
33
50
|
it('should filter values based on a predicate', () => {
|
|
34
51
|
const iterator = ExtraIterator.from([1, 2, 3, 4]).filter(x => x % 2 === 0);
|
|
@@ -42,6 +59,10 @@ describe(ExtraIterator.name, () => {
|
|
|
42
59
|
const iterator = ExtraIterator.from([1, 2, 3, 4]).take(2);
|
|
43
60
|
expect(iterator.toArray()).toEqual([1, 2]);
|
|
44
61
|
});
|
|
62
|
+
it('should take the last values of the sequence', () => {
|
|
63
|
+
const iterator = ExtraIterator.from([1, 2, 3, 4]).take(-2);
|
|
64
|
+
expect(iterator.toArray()).toEqual([3, 4]);
|
|
65
|
+
});
|
|
45
66
|
it('should drop a given number of values', () => {
|
|
46
67
|
const iterator = ExtraIterator.from([1, 2, 3, 4]).drop(2);
|
|
47
68
|
expect(iterator.toArray()).toEqual([3, 4]);
|
|
@@ -54,9 +75,19 @@ describe(ExtraIterator.name, () => {
|
|
|
54
75
|
const iterator = ExtraIterator.from([1, 2, 3]);
|
|
55
76
|
expect(iterator.first()).toBe(1);
|
|
56
77
|
});
|
|
57
|
-
it('should
|
|
58
|
-
const iterator = ExtraIterator.
|
|
59
|
-
expect(iterator.
|
|
78
|
+
it('should have a single value', () => {
|
|
79
|
+
const iterator = ExtraIterator.single(42);
|
|
80
|
+
expect(iterator.first()).toBe(42);
|
|
81
|
+
});
|
|
82
|
+
describe(ExtraIterator.prototype.last.name, () => {
|
|
83
|
+
it('should return the last value if the iterator is not empty', () => {
|
|
84
|
+
const iterator = ExtraIterator.from([1, 2, 3]);
|
|
85
|
+
expect(iterator.last()).toBe(3);
|
|
86
|
+
});
|
|
87
|
+
it('should return undefined if the iterator is empty', () => {
|
|
88
|
+
const iterator = ExtraIterator.empty();
|
|
89
|
+
expect(iterator.last()).toBeUndefined();
|
|
90
|
+
});
|
|
60
91
|
});
|
|
61
92
|
it('should return a value at a specific index', () => {
|
|
62
93
|
const iterator = ExtraIterator.from([1, 2, 3]);
|
|
@@ -92,6 +123,10 @@ describe(ExtraIterator.name, () => {
|
|
|
92
123
|
const iterator = ExtraIterator.from([2, 3]).prepend(1);
|
|
93
124
|
expect(iterator.toArray()).toEqual([1, 2, 3]);
|
|
94
125
|
});
|
|
126
|
+
it('should prepend multiple values to the iterator', () => {
|
|
127
|
+
const iterator = ExtraIterator.from([4, 5, 6]).prependAll([1, 2, 3]);
|
|
128
|
+
expect(iterator.toArray()).toEqual([1, 2, 3, 4, 5, 6]);
|
|
129
|
+
});
|
|
95
130
|
it('should append a value to the iterator', () => {
|
|
96
131
|
const iterator = ExtraIterator.from([1, 2]).append(3);
|
|
97
132
|
expect(iterator.toArray()).toEqual([1, 2, 3]);
|
|
@@ -114,9 +149,19 @@ describe(ExtraIterator.name, () => {
|
|
|
114
149
|
const iterator = ExtraIterator.from([1, 2, 3, 4]).splice(1, 2, 5, 6);
|
|
115
150
|
expect(iterator.toArray()).toEqual([1, 5, 6, 4]);
|
|
116
151
|
});
|
|
117
|
-
it('should
|
|
118
|
-
const iterator = ExtraIterator.
|
|
119
|
-
expect(iterator.toArray()).toEqual([
|
|
152
|
+
it('should splice values from the end of the iterator', () => {
|
|
153
|
+
const iterator = ExtraIterator.from([1, 2, 3, 4]).splice(-3, 2, 5, 6);
|
|
154
|
+
expect(iterator.toArray()).toEqual([1, 5, 6, 4]);
|
|
155
|
+
});
|
|
156
|
+
describe('defaultIfEmpty', () => {
|
|
157
|
+
it('should provide a default value if the iterator is empty', () => {
|
|
158
|
+
const iterator = ExtraIterator.empty().defaultIfEmpty(() => 42);
|
|
159
|
+
expect(iterator.toArray()).toEqual([42]);
|
|
160
|
+
});
|
|
161
|
+
it('should relay the iterator itself if it is not empty', () => {
|
|
162
|
+
const iterator = ExtraIterator.from([1, 2, 3]).defaultIfEmpty(() => 42);
|
|
163
|
+
expect(iterator.toArray()).toEqual([1, 2, 3]);
|
|
164
|
+
});
|
|
120
165
|
});
|
|
121
166
|
it('should group values by a key', () => {
|
|
122
167
|
const iterator = ExtraIterator.from(['apple', 'banana', 'apricot']);
|
|
@@ -171,6 +216,16 @@ describe(ExtraIterator.name, () => {
|
|
|
171
216
|
.toArray();
|
|
172
217
|
expect(result).toEqual([[1, 1], [2], [3, 3, 3], [2, 2]]);
|
|
173
218
|
});
|
|
219
|
+
it('should chunk using a key selector function', () => {
|
|
220
|
+
const result = ExtraIterator.from(['apple', 'apricot', 'banana', 'avocado'])
|
|
221
|
+
.chunkWith((lhs, rhs) => lhs[0] === rhs[0])
|
|
222
|
+
.toArray();
|
|
223
|
+
expect(result).toEqual([['apple', 'apricot'], ['banana'], ['avocado']]);
|
|
224
|
+
});
|
|
225
|
+
it('should build an empty iterator for an empty iterator', () => {
|
|
226
|
+
const result = ExtraIterator.from([]).chunkWith((lhs, rhs) => lhs === rhs).toArray();
|
|
227
|
+
expect(result).toEqual([]);
|
|
228
|
+
});
|
|
174
229
|
});
|
|
175
230
|
it('should create a chain of responsibility function', () => {
|
|
176
231
|
const humanizeDuration = ExtraIterator.from([
|
|
@@ -191,9 +246,18 @@ describe(ExtraIterator.name, () => {
|
|
|
191
246
|
const chain = handlers.toChainOfResponsibilityFunction(next => next());
|
|
192
247
|
expect(() => chain()).toThrow();
|
|
193
248
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
249
|
+
describe(ExtraIterator.prototype.loop.name, () => {
|
|
250
|
+
it('should repeat values', () => {
|
|
251
|
+
const iterator = ExtraIterator.from([1, 2, 3]).loop(3);
|
|
252
|
+
expect(iterator.toArray()).toEqual([1, 2, 3, 1, 2, 3, 1, 2, 3]);
|
|
253
|
+
});
|
|
254
|
+
it('should yield an empty iterator if the count is equal to or lower than 0', () => {
|
|
255
|
+
expect(ExtraIterator.from([1, 2, 3]).loop(0).toArray()).toEqual([]);
|
|
256
|
+
expect(ExtraIterator.from([1, 2, 3]).loop(-1).toArray()).toEqual([]);
|
|
257
|
+
expect(ExtraIterator.from([1, 2, 3]).loop(-3).toArray()).toEqual([]);
|
|
258
|
+
expect(ExtraIterator.from([1, 2, 3]).loop(-4).toArray()).toEqual([]);
|
|
259
|
+
expect(ExtraIterator.from([1, 2, 3]).loop(-99999).toArray()).toEqual([]);
|
|
260
|
+
});
|
|
197
261
|
});
|
|
198
262
|
describe(ExtraIterator.range.name, () => {
|
|
199
263
|
it('should iterate over ranges', () => {
|
|
@@ -216,4 +280,42 @@ describe(ExtraIterator.name, () => {
|
|
|
216
280
|
expect(() => ExtraIterator.range(0, 10, { step: 0 }).toArray()).toThrow();
|
|
217
281
|
});
|
|
218
282
|
});
|
|
283
|
+
it('should count the number of elements in the iterator', () => {
|
|
284
|
+
expect(ExtraIterator.from([1, 2, 3]).count()).toBe(3);
|
|
285
|
+
expect(ExtraIterator.empty().count()).toBe(0);
|
|
286
|
+
});
|
|
287
|
+
describe(ExtraIterator.prototype.testUnique.name, () => {
|
|
288
|
+
it('should return true if all values are unique', () => {
|
|
289
|
+
expect(ExtraIterator.from([1, 2, 3]).testUnique()).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
it('should return false if there are duplicate values', () => {
|
|
292
|
+
expect(ExtraIterator.from([1, 2, 2, 3]).testUnique()).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
it('should return true for an empty iterator', () => {
|
|
295
|
+
expect(ExtraIterator.empty().testUnique()).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
describe(ExtraIterator.prototype.withEach.name, () => {
|
|
299
|
+
it('should perform a side effect for each value', () => {
|
|
300
|
+
const sideEffects = [];
|
|
301
|
+
const iterator = ExtraIterator.from([1, 2, 3]).withEach(x => void sideEffects.push(x));
|
|
302
|
+
expect(iterator.toArray()).toEqual([1, 2, 3]);
|
|
303
|
+
expect(sideEffects).toEqual([1, 2, 3]);
|
|
304
|
+
});
|
|
305
|
+
it('should not perform side effects if the iterator is not consumed', () => {
|
|
306
|
+
const sideEffects = [];
|
|
307
|
+
ExtraIterator.from([1, 2, 3]).withEach(x => void sideEffects.push(x));
|
|
308
|
+
expect(sideEffects).toEqual([]);
|
|
309
|
+
});
|
|
310
|
+
it('should perform side effects even if the iterator is only partially consumed', () => {
|
|
311
|
+
const sideEffects = [];
|
|
312
|
+
const iterator = ExtraIterator.from([1, 2, 3]).withEach(x => void sideEffects.push(x));
|
|
313
|
+
iterator.next();
|
|
314
|
+
expect(sideEffects).toEqual([1]);
|
|
315
|
+
});
|
|
316
|
+
it('should perform side effects on objects', () => {
|
|
317
|
+
const iterator = ExtraIterator.from([{ value: 1 }, { value: 2 }, { value: 3 }]).withEach(obj => obj.value *= 2);
|
|
318
|
+
expect(iterator.toArray()).toEqual([{ value: 2 }, { value: 4 }, { value: 6 }]);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
219
321
|
});
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "An extension of the Iterator class with additional utility helper functions.",
|
|
4
4
|
"author": "Leonardo Raele <leonardoraele@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.13.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./dist/index.js",
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
"dist"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"test": "run-
|
|
16
|
+
"test": "run-p test:*",
|
|
17
17
|
"test:types": "tsc --noEmit",
|
|
18
18
|
"test:unit": "node --import=tsx --test **/*.test.ts",
|
|
19
|
+
"coverage": "run-s build coverage:test",
|
|
20
|
+
"coverage:test": "node --import=tsx --test --experimental-test-coverage",
|
|
19
21
|
"build": "tsc",
|
|
20
22
|
"prebuild": "rimraf dist",
|
|
21
23
|
"prepack": "run-s test build"
|