extra-iterator 0.10.3 → 0.11.1

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 CHANGED
@@ -46,12 +46,21 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
46
46
  *
47
47
  * @example
48
48
  * ExtraIterator.range(5, 10).toArray() // returns [5, 6, 7, 8, 9]
49
- * ExtraIterator.range(5, 10).append(10).toArray() // returns [5, 6, 7, 8, 9, 10]
50
- * ExtraIterator.range(1, 10, 2).toArray() // returns [1, 3, 5, 7, 9]
51
- * ExtraIterator.range(0, 1, 0.25).toArray() // returns [0, 0.25, 0.5, 0.75]
52
- * ExtraIterator.range(10, 0, -2).toArray() // returns [10, 8, 6, 4, 2]
49
+ * ExtraIterator.range(5, 10, { inclusive: true }).toArray() // returns [5, 6, 7, 8, 9, 10]
50
+ *
51
+ * // Counting down:
52
+ * ExtraIterator.range(10, 0).toArray() // return [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
53
+ * ExtraIterator.range(10, 0, { inclusive: true }).toArray() // return [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
54
+ *
55
+ * // Custom stepping value:
56
+ * ExtraIterator.range(1, 10, { step: 2 }).toArray() // returns [1, 3, 5, 7, 9]
57
+ * ExtraIterator.range(0, 1, { step: 0.25 }).toArray() // returns [0, 0.25, 0.5, 0.75]
58
+ * ExtraIterator.range(10, 0, { step: 2 }).toArray() // returns [10, 8, 6, 4, 2]
53
59
  */
54
- static range(start: number, end: number, step?: number): ExtraIterator<number>;
60
+ static range(start: number, end: number, { inclusive, step }?: {
61
+ inclusive?: boolean | undefined;
62
+ step?: number | undefined;
63
+ }): ExtraIterator<number>;
55
64
  /**
56
65
  * Creates an iterator that repeatedly yields the provided value.
57
66
  *
@@ -154,11 +163,11 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
154
163
  * @example
155
164
  *
156
165
  * ExtraIterator.from([4, 5, 6])
157
- * .prependMany([1, 2, 3])
166
+ * .prependAll([1, 2, 3])
158
167
  * .toArray()
159
168
  * // returns [1, 2, 3, 4, 5, 6]
160
169
  */
161
- prependMany<U>(items: Iterable<U>): ExtraIterator<T | U>;
170
+ prependAll<U>(items: Iterable<U>): ExtraIterator<T | U>;
162
171
  /**
163
172
  * Creates a new iterator that invokes the provided callback function over each element of this iterator and yields
164
173
  * the elements for which the callback returns `true`, only for as long as the callback returns `true`.
@@ -204,9 +213,9 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
204
213
  /**
205
214
  * Groups the elements in this iterator into groups of variable size.
206
215
  *
207
- * This method calls the provided predicate function for each pair of adjacent elements in this iterator. It should
208
- * return `true` if the elements should belong to the same group, or `false` if they should belong to different
209
- * groups.
216
+ * This method calls the provided predicate function for each pair of adjacent elements in this iterator. The
217
+ * function should return `true` if the elements should belong to the same group, or `false` if they should belong
218
+ * to different groups.
210
219
  *
211
220
  * The resulting iterator yields arrays of elements that belong to the same group.
212
221
  *
@@ -283,7 +292,8 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
283
292
  */
284
293
  splice(startIndex: number, deleteCount: number, ...newItems: T[]): ExtraIterator<T>;
285
294
  /**
286
- * Returns an iterator the provided element if this iterator is empty; otherwise, it returns this iterator.
295
+ * If this iterator is empty, returns an iterator with the provided element as its only element; otherwise, it
296
+ * returns a copy of this iterator.
287
297
  */
288
298
  defaultIfEmpty(provider: () => T): ExtraIterator<T>;
289
299
  /**
@@ -371,8 +381,8 @@ export declare class ExtraIterator<T> extends Iterator<T, any, any> {
371
381
  *
372
382
  * @example
373
383
  *
374
- * ExtraIterator.from([1, 2, 3]).uniqueness() // returns true
375
- * ExtraIterator.from([1, 2, 3, 1]).uniqueness() // returns false
384
+ * ExtraIterator.from([1, 2, 3]).testUnique() // returns true
385
+ * ExtraIterator.from([1, 2, 3, 1]).testUnique() // returns false
376
386
  */
377
387
  testUnique(mapper?: (value: T) => unknown): boolean;
378
388
  /**
package/dist/index.js CHANGED
@@ -54,23 +54,30 @@ export class ExtraIterator extends Iterator {
54
54
  *
55
55
  * @example
56
56
  * ExtraIterator.range(5, 10).toArray() // returns [5, 6, 7, 8, 9]
57
- * ExtraIterator.range(5, 10).append(10).toArray() // returns [5, 6, 7, 8, 9, 10]
58
- * ExtraIterator.range(1, 10, 2).toArray() // returns [1, 3, 5, 7, 9]
59
- * ExtraIterator.range(0, 1, 0.25).toArray() // returns [0, 0.25, 0.5, 0.75]
60
- * ExtraIterator.range(10, 0, -2).toArray() // returns [10, 8, 6, 4, 2]
57
+ * ExtraIterator.range(5, 10, { inclusive: true }).toArray() // returns [5, 6, 7, 8, 9, 10]
58
+ *
59
+ * // Counting down:
60
+ * ExtraIterator.range(10, 0).toArray() // return [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
61
+ * ExtraIterator.range(10, 0, { inclusive: true }).toArray() // return [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
62
+ *
63
+ * // Custom stepping value:
64
+ * ExtraIterator.range(1, 10, { step: 2 }).toArray() // returns [1, 3, 5, 7, 9]
65
+ * ExtraIterator.range(0, 1, { step: 0.25 }).toArray() // returns [0, 0.25, 0.5, 0.75]
66
+ * ExtraIterator.range(10, 0, { step: 2 }).toArray() // returns [10, 8, 6, 4, 2]
61
67
  */
62
- static range(start, end, step = 1) {
68
+ static range(start, end, { inclusive = false, step = 1 } = {}) {
63
69
  return ExtraIterator.from(function* () {
64
- if (Math.abs(step) < Number.EPSILON) {
70
+ step = Math.abs(step);
71
+ if (step < Number.EPSILON) {
65
72
  throw new Error('Failed to create range. Cause: Range step cannot be 0.');
66
73
  }
67
- else if (step > 0) {
68
- for (let i = start; i < end; i += step) {
74
+ else if (end > start) {
75
+ for (let i = start; inclusive ? i <= end : i < end; i += step) {
69
76
  yield i;
70
77
  }
71
78
  }
72
79
  else {
73
- for (let i = start; i > end; i += step) {
80
+ for (let i = start; inclusive ? i >= end : i > end; i -= step) {
74
81
  yield i;
75
82
  }
76
83
  }
@@ -264,11 +271,11 @@ export class ExtraIterator extends Iterator {
264
271
  * @example
265
272
  *
266
273
  * ExtraIterator.from([4, 5, 6])
267
- * .prependMany([1, 2, 3])
274
+ * .prependAll([1, 2, 3])
268
275
  * .toArray()
269
276
  * // returns [1, 2, 3, 4, 5, 6]
270
277
  */
271
- prependMany(items) {
278
+ prependAll(items) {
272
279
  return ExtraIterator.from(function* () {
273
280
  yield* items;
274
281
  yield* this;
@@ -344,9 +351,9 @@ export class ExtraIterator extends Iterator {
344
351
  /**
345
352
  * Groups the elements in this iterator into groups of variable size.
346
353
  *
347
- * This method calls the provided predicate function for each pair of adjacent elements in this iterator. It should
348
- * return `true` if the elements should belong to the same group, or `false` if they should belong to different
349
- * groups.
354
+ * This method calls the provided predicate function for each pair of adjacent elements in this iterator. The
355
+ * function should return `true` if the elements should belong to the same group, or `false` if they should belong
356
+ * to different groups.
350
357
  *
351
358
  * The resulting iterator yields arrays of elements that belong to the same group.
352
359
  *
@@ -366,7 +373,7 @@ export class ExtraIterator extends Iterator {
366
373
  let chunk = [first.value];
367
374
  for (let left = first, right, index = 0; right = this.next(), !right.done; left = right, index++) {
368
375
  if (predicate(left.value, right.value, index, chunk)) {
369
- chunk.push(left.value);
376
+ chunk.push(right.value);
370
377
  }
371
378
  else {
372
379
  yield chunk;
@@ -482,8 +489,7 @@ export class ExtraIterator extends Iterator {
482
489
  */
483
490
  splice(startIndex, deleteCount, ...newItems) {
484
491
  if (startIndex < 0) {
485
- return ExtraIterator.from(this.toArray()
486
- .toSpliced(startIndex, deleteCount, ...newItems));
492
+ return ExtraIterator.from(this.toArray().toSpliced(startIndex, deleteCount, ...newItems));
487
493
  }
488
494
  return ExtraIterator.from(function* () {
489
495
  for (let index = 0, next; next = this.next(), !next.done; index++) {
@@ -497,7 +503,8 @@ export class ExtraIterator extends Iterator {
497
503
  }.call(this));
498
504
  }
499
505
  /**
500
- * Returns an iterator the provided element if this iterator is empty; otherwise, it returns this iterator.
506
+ * If this iterator is empty, returns an iterator with the provided element as its only element; otherwise, it
507
+ * returns a copy of this iterator.
501
508
  */
502
509
  defaultIfEmpty(provider) {
503
510
  return ExtraIterator.from(function* () {
@@ -521,9 +528,6 @@ export class ExtraIterator extends Iterator {
521
528
  loop(times = Infinity) {
522
529
  return ExtraIterator.from(function* () {
523
530
  const values = this.toArray();
524
- if (values.length === 0) {
525
- return;
526
- }
527
531
  for (let i = 0; i < times; i++) {
528
532
  yield* values;
529
533
  }
@@ -656,8 +660,8 @@ export class ExtraIterator extends Iterator {
656
660
  *
657
661
  * @example
658
662
  *
659
- * ExtraIterator.from([1, 2, 3]).uniqueness() // returns true
660
- * ExtraIterator.from([1, 2, 3, 1]).uniqueness() // returns false
663
+ * ExtraIterator.from([1, 2, 3]).testUnique() // returns true
664
+ * ExtraIterator.from([1, 2, 3, 1]).testUnique() // returns false
661
665
  */
662
666
  testUnique(mapper) {
663
667
  const seen = new Set();
@@ -1,18 +1,30 @@
1
1
  import { ExtraIterator } from './index';
2
2
  import { describe, it } from 'node:test';
3
3
  import { expect } from 'expect';
4
- describe('ExtraIterator', () => {
4
+ describe(ExtraIterator.name, () => {
5
5
  it('should create an ExtraIterator from an array', () => {
6
6
  const array = [1, 2, 3];
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 = ExtraIterator.zip(a, b);
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]);
@@ -42,6 +54,10 @@ describe('ExtraIterator', () => {
42
54
  const iterator = ExtraIterator.from([1, 2, 3, 4]).take(2);
43
55
  expect(iterator.toArray()).toEqual([1, 2]);
44
56
  });
57
+ it('should take the last values of the sequence', () => {
58
+ const iterator = ExtraIterator.from([1, 2, 3, 4]).take(-2);
59
+ expect(iterator.toArray()).toEqual([3, 4]);
60
+ });
45
61
  it('should drop a given number of values', () => {
46
62
  const iterator = ExtraIterator.from([1, 2, 3, 4]).drop(2);
47
63
  expect(iterator.toArray()).toEqual([3, 4]);
@@ -54,9 +70,15 @@ describe('ExtraIterator', () => {
54
70
  const iterator = ExtraIterator.from([1, 2, 3]);
55
71
  expect(iterator.first()).toBe(1);
56
72
  });
57
- it('should return the last value', () => {
58
- const iterator = ExtraIterator.from([1, 2, 3]);
59
- expect(iterator.last()).toBe(3);
73
+ describe(ExtraIterator.prototype.last.name, () => {
74
+ it('should return the last value if the iterator is not empty', () => {
75
+ const iterator = ExtraIterator.from([1, 2, 3]);
76
+ expect(iterator.last()).toBe(3);
77
+ });
78
+ it('should return undefined if the iterator is empty', () => {
79
+ const iterator = ExtraIterator.empty();
80
+ expect(iterator.last()).toBeUndefined();
81
+ });
60
82
  });
61
83
  it('should return a value at a specific index', () => {
62
84
  const iterator = ExtraIterator.from([1, 2, 3]);
@@ -92,6 +114,10 @@ describe('ExtraIterator', () => {
92
114
  const iterator = ExtraIterator.from([2, 3]).prepend(1);
93
115
  expect(iterator.toArray()).toEqual([1, 2, 3]);
94
116
  });
117
+ it('should prepend multiple values to the iterator', () => {
118
+ const iterator = ExtraIterator.from([4, 5, 6]).prependAll([1, 2, 3]);
119
+ expect(iterator.toArray()).toEqual([1, 2, 3, 4, 5, 6]);
120
+ });
95
121
  it('should append a value to the iterator', () => {
96
122
  const iterator = ExtraIterator.from([1, 2]).append(3);
97
123
  expect(iterator.toArray()).toEqual([1, 2, 3]);
@@ -114,9 +140,19 @@ describe('ExtraIterator', () => {
114
140
  const iterator = ExtraIterator.from([1, 2, 3, 4]).splice(1, 2, 5, 6);
115
141
  expect(iterator.toArray()).toEqual([1, 5, 6, 4]);
116
142
  });
117
- it('should provide a default value if the iterator is empty', () => {
118
- const iterator = ExtraIterator.empty().defaultIfEmpty(() => 42);
119
- expect(iterator.toArray()).toEqual([42]);
143
+ it('should splice values from the end of the iterator', () => {
144
+ const iterator = ExtraIterator.from([1, 2, 3, 4]).splice(-3, 2, 5, 6);
145
+ expect(iterator.toArray()).toEqual([1, 5, 6, 4]);
146
+ });
147
+ describe('defaultIfEmpty', () => {
148
+ it('should provide a default value if the iterator is empty', () => {
149
+ const iterator = ExtraIterator.empty().defaultIfEmpty(() => 42);
150
+ expect(iterator.toArray()).toEqual([42]);
151
+ });
152
+ it('should relay the iterator itself if it is not empty', () => {
153
+ const iterator = ExtraIterator.from([1, 2, 3]).defaultIfEmpty(() => 42);
154
+ expect(iterator.toArray()).toEqual([1, 2, 3]);
155
+ });
120
156
  });
121
157
  it('should group values by a key', () => {
122
158
  const iterator = ExtraIterator.from(['apple', 'banana', 'apricot']);
@@ -171,6 +207,16 @@ describe('ExtraIterator', () => {
171
207
  .toArray();
172
208
  expect(result).toEqual([[1, 1], [2], [3, 3, 3], [2, 2]]);
173
209
  });
210
+ it('should chunk using a key selector function', () => {
211
+ const result = ExtraIterator.from(['apple', 'apricot', 'banana', 'avocado'])
212
+ .chunkWith((lhs, rhs) => lhs[0] === rhs[0])
213
+ .toArray();
214
+ expect(result).toEqual([['apple', 'apricot'], ['banana'], ['avocado']]);
215
+ });
216
+ it('should build an empty iterator for an empty iterator', () => {
217
+ const result = ExtraIterator.from([]).chunkWith((lhs, rhs) => lhs === rhs).toArray();
218
+ expect(result).toEqual([]);
219
+ });
174
220
  });
175
221
  it('should create a chain of responsibility function', () => {
176
222
  const humanizeDuration = ExtraIterator.from([
@@ -191,15 +237,76 @@ describe('ExtraIterator', () => {
191
237
  const chain = handlers.toChainOfResponsibilityFunction(next => next());
192
238
  expect(() => chain()).toThrow();
193
239
  });
194
- it('should repeat values', () => {
195
- const iterator = ExtraIterator.from([1, 2, 3]).loop(3);
196
- expect(iterator.toArray()).toEqual([1, 2, 3, 1, 2, 3, 1, 2, 3]);
240
+ describe(ExtraIterator.prototype.loop.name, () => {
241
+ it('should repeat values', () => {
242
+ const iterator = ExtraIterator.from([1, 2, 3]).loop(3);
243
+ expect(iterator.toArray()).toEqual([1, 2, 3, 1, 2, 3, 1, 2, 3]);
244
+ });
245
+ it('should yield an empty iterator if the count is equal to or lower than 0', () => {
246
+ expect(ExtraIterator.from([1, 2, 3]).loop(0).toArray()).toEqual([]);
247
+ expect(ExtraIterator.from([1, 2, 3]).loop(-1).toArray()).toEqual([]);
248
+ expect(ExtraIterator.from([1, 2, 3]).loop(-3).toArray()).toEqual([]);
249
+ expect(ExtraIterator.from([1, 2, 3]).loop(-4).toArray()).toEqual([]);
250
+ expect(ExtraIterator.from([1, 2, 3]).loop(-99999).toArray()).toEqual([]);
251
+ });
252
+ });
253
+ describe(ExtraIterator.range.name, () => {
254
+ it('should iterate over ranges', () => {
255
+ expect(ExtraIterator.range(5, 10).toArray()).toEqual([5, 6, 7, 8, 9]);
256
+ expect(ExtraIterator.range(5, 10).append(10).toArray()).toEqual([5, 6, 7, 8, 9, 10]);
257
+ });
258
+ it('should iterate over ranges with steps', () => {
259
+ expect(ExtraIterator.range(1, 10, { step: 2 }).toArray()).toEqual([1, 3, 5, 7, 9]);
260
+ expect(ExtraIterator.range(0, 1, { step: 0.25 }).toArray()).toEqual([0, 0.25, 0.5, 0.75]);
261
+ expect(ExtraIterator.range(10, 0, { step: 2 }).toArray()).toEqual([10, 8, 6, 4, 2]);
262
+ expect(ExtraIterator.range(10, 0, { step: -2 }).toArray()).toEqual([10, 8, 6, 4, 2]);
263
+ });
264
+ it('should iterate over inclusive ranges', () => {
265
+ expect(ExtraIterator.range(1, 10, { inclusive: true }).toArray()).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
266
+ expect(ExtraIterator.range(1, 10, { inclusive: true, step: 2 }).toArray()).toEqual([1, 3, 5, 7, 9]);
267
+ expect(ExtraIterator.range(0, 1, { inclusive: true, step: 0.25 }).toArray()).toEqual([0, 0.25, 0.5, 0.75, 1]);
268
+ expect(ExtraIterator.range(10, 0, { inclusive: true, step: -2 }).toArray()).toEqual([10, 8, 6, 4, 2, 0]);
269
+ });
270
+ it('should throw an error if the step is 0', () => {
271
+ expect(() => ExtraIterator.range(0, 10, { step: 0 }).toArray()).toThrow();
272
+ });
273
+ });
274
+ it('should count the number of elements in the iterator', () => {
275
+ expect(ExtraIterator.from([1, 2, 3]).count()).toBe(3);
276
+ expect(ExtraIterator.empty().count()).toBe(0);
197
277
  });
198
- it('should iterate over ranges', () => {
199
- expect(ExtraIterator.range(5, 10).toArray()).toEqual([5, 6, 7, 8, 9]);
200
- expect(ExtraIterator.range(5, 10).append(10).toArray()).toEqual([5, 6, 7, 8, 9, 10]);
201
- expect(ExtraIterator.range(1, 10, 2).toArray()).toEqual([1, 3, 5, 7, 9]);
202
- expect(ExtraIterator.range(0, 1, 0.25).toArray()).toEqual([0, 0.25, 0.5, 0.75]);
203
- expect(ExtraIterator.range(10, 0, -2).toArray()).toEqual([10, 8, 6, 4, 2]);
278
+ describe(ExtraIterator.prototype.testUnique.name, () => {
279
+ it('should return true if all values are unique', () => {
280
+ expect(ExtraIterator.from([1, 2, 3]).testUnique()).toBe(true);
281
+ });
282
+ it('should return false if there are duplicate values', () => {
283
+ expect(ExtraIterator.from([1, 2, 2, 3]).testUnique()).toBe(false);
284
+ });
285
+ it('should return true for an empty iterator', () => {
286
+ expect(ExtraIterator.empty().testUnique()).toBe(true);
287
+ });
288
+ });
289
+ describe(ExtraIterator.prototype.withEach.name, () => {
290
+ it('should perform a side effect for each value', () => {
291
+ const sideEffects = [];
292
+ const iterator = ExtraIterator.from([1, 2, 3]).withEach(x => void sideEffects.push(x));
293
+ expect(iterator.toArray()).toEqual([1, 2, 3]);
294
+ expect(sideEffects).toEqual([1, 2, 3]);
295
+ });
296
+ it('should not perform side effects if the iterator is not consumed', () => {
297
+ const sideEffects = [];
298
+ ExtraIterator.from([1, 2, 3]).withEach(x => void sideEffects.push(x));
299
+ expect(sideEffects).toEqual([]);
300
+ });
301
+ it('should perform side effects even if the iterator is only partially consumed', () => {
302
+ const sideEffects = [];
303
+ const iterator = ExtraIterator.from([1, 2, 3]).withEach(x => void sideEffects.push(x));
304
+ iterator.next();
305
+ expect(sideEffects).toEqual([1]);
306
+ });
307
+ it('should perform side effects on objects', () => {
308
+ const iterator = ExtraIterator.from([{ value: 1 }, { value: 2 }, { value: 3 }]).withEach(obj => obj.value *= 2);
309
+ expect(iterator.toArray()).toEqual([{ value: 2 }, { value: 4 }, { value: 6 }]);
310
+ });
204
311
  });
205
312
  });
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.10.3",
6
+ "version": "0.11.1",
7
7
  "type": "module",
8
8
  "exports": {
9
9
  ".": "./dist/index.js",