@byloth/core 2.0.0-rc.9 → 2.0.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.
Files changed (47) hide show
  1. package/dist/core.js +4087 -621
  2. package/dist/core.js.map +1 -1
  3. package/dist/core.umd.cjs +2 -2
  4. package/dist/core.umd.cjs.map +1 -1
  5. package/package.json +17 -13
  6. package/src/core/types.ts +41 -0
  7. package/src/helpers.ts +11 -2
  8. package/src/index.ts +12 -9
  9. package/src/models/aggregators/aggregated-async-iterator.ts +920 -21
  10. package/src/models/aggregators/aggregated-iterator.ts +838 -22
  11. package/src/models/aggregators/reduced-iterator.ts +827 -11
  12. package/src/models/aggregators/types.ts +153 -10
  13. package/src/models/callbacks/callable-object.ts +42 -6
  14. package/src/models/callbacks/index.ts +2 -2
  15. package/src/models/callbacks/publisher.ts +160 -4
  16. package/src/models/callbacks/switchable-callback.ts +230 -23
  17. package/src/models/callbacks/types.ts +16 -0
  18. package/src/models/exceptions/core.ts +132 -3
  19. package/src/models/exceptions/index.ts +405 -13
  20. package/src/models/index.ts +4 -8
  21. package/src/models/iterators/smart-async-iterator.ts +827 -22
  22. package/src/models/iterators/smart-iterator.ts +755 -20
  23. package/src/models/iterators/types.ts +268 -9
  24. package/src/models/json/json-storage.ts +508 -110
  25. package/src/models/json/types.ts +10 -1
  26. package/src/models/promises/deferred-promise.ts +85 -5
  27. package/src/models/promises/index.ts +1 -3
  28. package/src/models/promises/smart-promise.ts +272 -4
  29. package/src/models/promises/timed-promise.ts +43 -1
  30. package/src/models/promises/types.ts +84 -2
  31. package/src/models/timers/clock.ts +109 -19
  32. package/src/models/timers/countdown.ts +176 -21
  33. package/src/models/timers/game-loop.ts +266 -0
  34. package/src/models/timers/index.ts +2 -1
  35. package/src/models/types.ts +6 -5
  36. package/src/utils/async.ts +43 -0
  37. package/src/utils/curve.ts +85 -0
  38. package/src/utils/date.ts +204 -10
  39. package/src/utils/dom.ts +16 -2
  40. package/src/utils/index.ts +3 -2
  41. package/src/utils/iterator.ts +200 -17
  42. package/src/utils/math.ts +55 -3
  43. package/src/utils/random.ts +139 -2
  44. package/src/utils/string.ts +11 -0
  45. package/src/models/game-loop.ts +0 -83
  46. package/src/models/promises/long-running-task.ts +0 -294
  47. package/src/models/promises/thenable.ts +0 -97
@@ -1,18 +1,119 @@
1
1
  import AggregatedIterator from "../aggregators/aggregated-iterator.js";
2
2
  import { ValueException } from "../exceptions/index.js";
3
3
 
4
- import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IteratorLike } from "./types.js";
4
+ import type { GeneratorFunction, Iteratee, TypeGuardPredicate, Reducer, IteratorLike } from "./types.js";
5
5
 
6
+ /**
7
+ * A wrapper class representing an enhanced and instantiable version
8
+ * of the native {@link Iterable} & {@link Iterator} interfaces.
9
+ *
10
+ * It provides a set of utility methods to better manipulate and
11
+ * transform iterators in a functional and highly performant way.
12
+ * It takes inspiration from the native {@link Array} methods like
13
+ * {@link Array.map}, {@link Array.filter}, {@link Array.reduce}, etc...
14
+ *
15
+ * The class is lazy, meaning that the transformations are applied
16
+ * only when the resulting iterator is materialized, not before.
17
+ * This allows to chain multiple transformations without
18
+ * the need to iterate over the elements multiple times.
19
+ *
20
+ * ```ts
21
+ * const result = new SmartIterator<number>(["-5", "-4", "-3", "-2", "-1", "0", "1", "2", "3", "4", "5"])
22
+ * .map(Number)
23
+ * .map((value) => value + Math.ceil(Math.abs(value / 2)))
24
+ * .filter((value) => value >= 0)
25
+ * .map((value) => value + 1)
26
+ * .reduce((acc, value) => acc + value);
27
+ *
28
+ * console.log(result); // 31
29
+ * ```
30
+ *
31
+ * @template T The type of elements in the iterator.
32
+ * @template R The type of the final result of the iterator. Default is `void`.
33
+ * @template N The type of the argument required by the `next` method. Default is `undefined`.
34
+ */
6
35
  export default class SmartIterator<T, R = void, N = undefined> implements Iterator<T, R, N>
7
36
  {
37
+ /**
38
+ * The native {@link Iterator} object that is being wrapped by this instance.
39
+ */
8
40
  protected _iterator: Iterator<T, R, N>;
9
41
 
10
- public return?: (value?: R) => IteratorResult<T, R>;
11
- public throw?: (error?: unknown) => IteratorResult<T, R>;
12
-
42
+ /**
43
+ * Initializes a new instance of the {@link SmartIterator} class.
44
+ *
45
+ * ---
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const iterator = new SmartIterator<string>(["A", "B", "C"]);
50
+ * ```
51
+ *
52
+ * ---
53
+ *
54
+ * @param iterable The iterable object to wrap.
55
+ */
13
56
  public constructor(iterable: Iterable<T, R, N>);
57
+
58
+ /**
59
+ * Initializes a new instance of the {@link SmartIterator} class.
60
+ *
61
+ * ---
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * const iterator = new SmartIterator<number, void, number>({
66
+ * _sum: 0, _count: 0,
67
+ *
68
+ * next: function(value: number)
69
+ * {
70
+ * this._sum += value;
71
+ * this._count += 1;
72
+ *
73
+ * return { done: false, value: this._sum / this._count };
74
+ * }
75
+ * })
76
+ * ```
77
+ *
78
+ * ---
79
+ *
80
+ * @param iterator The iterator object to wrap.
81
+ */
14
82
  public constructor(iterator: Iterator<T, R, N>);
83
+
84
+ /**
85
+ * Initializes a new instance of the {@link SmartIterator} class.
86
+ *
87
+ * ---
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const iterator = new SmartIterator<number>(function* ()
92
+ * {
93
+ * for (let i = 2; i < 65_536; i *= 2) { yield (i - 1); }
94
+ * });
95
+ * ```
96
+ *
97
+ * ---
98
+ *
99
+ * @param generatorFn The generator function to wrap.
100
+ */
15
101
  public constructor(generatorFn: GeneratorFunction<T, R, N>);
102
+
103
+ /**
104
+ * Initializes a new instance of the {@link SmartIterator} class.
105
+ *
106
+ * ---
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const iterator = new SmartIterator(values);
111
+ * ```
112
+ *
113
+ * ---
114
+ *
115
+ * @param argument The iterable, iterator or generator function to wrap.
116
+ */
16
117
  public constructor(argument: IteratorLike<T, R, N> | GeneratorFunction<T, R, N>);
17
118
  public constructor(argument: IteratorLike<T, R, N> | GeneratorFunction<T, R, N>)
18
119
  {
@@ -28,11 +129,37 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
28
129
  {
29
130
  this._iterator = argument;
30
131
  }
31
-
32
- if (this._iterator.return) { this.return = (value) => this._iterator.return!(value); }
33
- if (this._iterator.throw) { this.throw = (error) => this._iterator.throw!(error); }
34
132
  }
35
133
 
134
+ /**
135
+ * Determines whether all elements of the iterator satisfy a given condition.
136
+ * See also {@link SmartIterator.some}.
137
+ *
138
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
139
+ * Once a single element doesn't satisfy the condition, the method will return `false` immediately.
140
+ *
141
+ * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
142
+ * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
143
+ * Consider using {@link SmartIterator.find} instead.
144
+ *
145
+ * If the iterator is infinite and every element satisfies the condition, the method will never return.
146
+ *
147
+ * ---
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
152
+ * const result = iterator.every((value) => value < 0);
153
+ *
154
+ * console.log(result); // false
155
+ * ```
156
+ *
157
+ * ---
158
+ *
159
+ * @param predicate The condition to check for each element of the iterator.
160
+ *
161
+ * @returns `true` if all elements satisfy the condition, `false` otherwise.
162
+ */
36
163
  public every(predicate: Iteratee<T, boolean>): boolean
37
164
  {
38
165
  let index = 0;
@@ -47,6 +174,36 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
47
174
  index += 1;
48
175
  }
49
176
  }
177
+
178
+ /**
179
+ * Determines whether any element of the iterator satisfies a given condition.
180
+ * See also {@link SmartIterator.every}.
181
+ *
182
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
183
+ * Once a single element satisfies the condition, the method will return `true` immediately.
184
+ *
185
+ * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
186
+ * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
187
+ * Consider using {@link SmartIterator.find} instead.
188
+ *
189
+ * If the iterator is infinite and no element satisfies the condition, the method will never return.
190
+ *
191
+ * ---
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
196
+ * const result = iterator.some((value) => value < 0);
197
+ *
198
+ * console.log(result); // true
199
+ * ```
200
+ *
201
+ * ---
202
+ *
203
+ * @param predicate The condition to check for each element of the iterator.
204
+ *
205
+ * @returns `true` if any element satisfies the condition, `false` otherwise.
206
+ */
50
207
  public some(predicate: Iteratee<T, boolean>): boolean
51
208
  {
52
209
  let index = 0;
@@ -62,8 +219,73 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
62
219
  }
63
220
  }
64
221
 
222
+ /**
223
+ * Filters the elements of the iterator using a given condition.
224
+ *
225
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
226
+ * If the condition is met, the element will be included in the new iterator.
227
+ *
228
+ * Since the iterator is lazy, the filtering process will
229
+ * be executed once the resulting iterator is materialized.
230
+ *
231
+ * A new iterator will be created, holding the reference to the original one.
232
+ * This means that the original iterator won't be consumed until the
233
+ * new one is and that consuming one of them will consume the other as well.
234
+ *
235
+ * ---
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
240
+ * const result = iterator.filter((value) => value < 0);
241
+ *
242
+ * console.log(result.toArray()); // [-2, -1]
243
+ * ```
244
+ *
245
+ * ---
246
+ *
247
+ * @param predicate The condition to check for each element of the iterator.
248
+ *
249
+ * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition.
250
+ */
65
251
  public filter(predicate: Iteratee<T, boolean>): SmartIterator<T, R>;
66
- public filter<S extends T>(predicate: TypeGuardIteratee<T, S>): SmartIterator<S, R>;
252
+
253
+ /**
254
+ * Filters the elements of the iterator using a given condition.
255
+ *
256
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
257
+ * If the condition is met, the element will be included in the new iterator.
258
+ *
259
+ * Since the iterator is lazy, the filtering process will
260
+ * be executed once the resulting iterator is materialized.
261
+ *
262
+ * A new iterator will be created, holding the reference to the original one.
263
+ * This means that the original iterator won't be consumed until the
264
+ * new one is and that consuming one of them will consume the other as well.
265
+ *
266
+ * ---
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * const iterator = new SmartIterator<number | string>([-2, "-1", "0", 1, "2"]);
271
+ * const result = iterator.filter<number>((value) => typeof value === "number");
272
+ *
273
+ * console.log(result.toArray()); // [-2, 1]
274
+ * ```
275
+ *
276
+ * ---
277
+ *
278
+ * @template S
279
+ * The type of the elements that satisfy the condition.
280
+ * This allows the type-system to infer the correct type of the new iterator.
281
+ *
282
+ * It must be a subtype of the original type of the elements.
283
+ *
284
+ * @param predicate The type guard condition to check for each element of the iterator.
285
+ *
286
+ * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition.
287
+ */
288
+ public filter<S extends T>(predicate: TypeGuardPredicate<T, S>): SmartIterator<S, R>;
67
289
  public filter(predicate: Iteratee<T, boolean>): SmartIterator<T, R>
68
290
  {
69
291
  const iterator = this._iterator;
@@ -71,11 +293,9 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
71
293
  return new SmartIterator<T, R>(function* ()
72
294
  {
73
295
  let index = 0;
74
-
75
296
  while (true)
76
297
  {
77
298
  const result = iterator.next();
78
-
79
299
  if (result.done) { return result.value; }
80
300
  if (predicate(result.value, index)) { yield result.value; }
81
301
 
@@ -83,6 +303,38 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
83
303
  }
84
304
  });
85
305
  }
306
+
307
+ /**
308
+ * Maps the elements of the iterator using a given transformation function.
309
+ *
310
+ * This method will iterate over all elements of the iterator applying the transformation function.
311
+ * The result of each transformation will be included in the new iterator.
312
+ *
313
+ * Since the iterator is lazy, the mapping process will
314
+ * be executed once the resulting iterator is materialized.
315
+ *
316
+ * A new iterator will be created, holding the reference to the original one.
317
+ * This means that the original iterator won't be consumed until the
318
+ * new one is and that consuming one of them will consume the other as well.
319
+ *
320
+ * ---
321
+ *
322
+ * @example
323
+ * ```ts
324
+ * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
325
+ * const result = iterator.map((value) => Math.abs(value));
326
+ *
327
+ * console.log(result.toArray()); // [2, 1, 0, 1, 2]
328
+ * ```
329
+ *
330
+ * ---
331
+ *
332
+ * @template V The type of the elements after the transformation.
333
+ *
334
+ * @param iteratee The transformation function to apply to each element of the iterator.
335
+ *
336
+ * @returns A new {@link SmartIterator} containing the transformed elements.
337
+ */
86
338
  public map<V>(iteratee: Iteratee<T, V>): SmartIterator<V, R>
87
339
  {
88
340
  const iterator = this._iterator;
@@ -90,7 +342,6 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
90
342
  return new SmartIterator<V, R>(function* ()
91
343
  {
92
344
  let index = 0;
93
-
94
345
  while (true)
95
346
  {
96
347
  const result = iterator.next();
@@ -102,7 +353,70 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
102
353
  }
103
354
  });
104
355
  }
356
+
357
+ /**
358
+ * Reduces the elements of the iterator using a given reducer function.
359
+ * This method will consume the entire iterator in the process.
360
+ *
361
+ * It will iterate over all elements of the iterator applying the reducer function.
362
+ * The result of each iteration will be passed as the accumulator to the next one.
363
+ *
364
+ * The first accumulator value will be the first element of the iterator.
365
+ * The last accumulator value will be the final result of the reduction.
366
+ *
367
+ * Also note that:
368
+ * - If an empty iterator is provided, a {@link ValueException} will be thrown.
369
+ * - If the iterator is infinite, the method will never return.
370
+ *
371
+ * ---
372
+ *
373
+ * @example
374
+ * ```ts
375
+ * const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);
376
+ * const result = iterator.reduce((acc, value) => acc + value);
377
+ *
378
+ * console.log(result); // 15
379
+ * ```
380
+ *
381
+ * ---
382
+ *
383
+ * @param reducer The reducer function to apply to each element of the iterator.
384
+ *
385
+ * @returns The final result of the reduction.
386
+ */
105
387
  public reduce(reducer: Reducer<T, T>): T;
388
+
389
+ /**
390
+ * Reduces the elements of the iterator using a given reducer function.
391
+ * This method will consume the entire iterator in the process.
392
+ *
393
+ * It will iterate over all elements of the iterator applying the reducer function.
394
+ * The result of each iteration will be passed as the accumulator to the next one.
395
+ *
396
+ * The first accumulator value will be the provided initial value.
397
+ * The last accumulator value will be the final result of the reduction.
398
+ *
399
+ * If the iterator is infinite, the method will never return.
400
+ *
401
+ * ---
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);
406
+ * const result = iterator.reduce((acc, value) => acc + value, 10);
407
+ *
408
+ * console.log(result); // 25
409
+ * ```
410
+ *
411
+ * ---
412
+ *
413
+ * @template A The type of the accumulator value which will also be the type of the final result of the reduction.
414
+ *
415
+ * @param reducer The reducer function to apply to each element of the iterator.
416
+ * @param initialValue The initial value of the accumulator.
417
+ *
418
+ * @returns The final result of the reduction.
419
+ */
106
420
  public reduce<A>(reducer: Reducer<T, A>, initialValue: A): A;
107
421
  public reduce<A>(reducer: Reducer<T, A>, initialValue?: A): A
108
422
  {
@@ -128,30 +442,92 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
128
442
  }
129
443
  }
130
444
 
131
- public flatMap<V>(iteratee: Iteratee<T, Iterable<V>>): SmartIterator<V, R>
445
+ /**
446
+ * Flattens the elements of the iterator using a given transformation function.
447
+ *
448
+ * This method will iterate over all elements of the iterator applying the transformation function.
449
+ * The result of each transformation will be flattened into the new iterator.
450
+ *
451
+ * Since the iterator is lazy, the flattening process will
452
+ * be executed once the resulting iterator is materialized.
453
+ *
454
+ * A new iterator will be created, holding the reference to the original one.
455
+ * This means that the original iterator won't be consumed until the
456
+ * new one is and that consuming one of them will consume the other as well.
457
+ *
458
+ * ---
459
+ *
460
+ * @example
461
+ * ```ts
462
+ * const iterator = new SmartIterator<number[]>([[-2, -1], 0, 1, 2, [3, 4, 5]]);
463
+ * const result = iterator.flatMap((value) => value);
464
+ *
465
+ * console.log(result.toArray()); // [-2, -1, 0, 1, 2, 3, 4, 5]
466
+ * ```
467
+ *
468
+ * ---
469
+ *
470
+ * @template V The type of the elements after the transformation.
471
+ *
472
+ * @param iteratee The transformation function to apply to each element of the iterator.
473
+ *
474
+ * @returns A new {@link SmartIterator} containing the flattened elements.
475
+ */
476
+ public flatMap<V>(iteratee: Iteratee<T, V | readonly V[]>): SmartIterator<V, R>
132
477
  {
133
478
  const iterator = this._iterator;
134
479
 
135
480
  return new SmartIterator<V, R>(function* ()
136
481
  {
137
482
  let index = 0;
138
-
139
483
  while (true)
140
484
  {
141
485
  const result = iterator.next();
142
486
  if (result.done) { return result.value; }
143
487
 
144
- const iterable = iteratee(result.value, index);
145
- for (const value of iterable)
488
+ const elements = iteratee(result.value, index);
489
+ if (elements instanceof Array)
146
490
  {
147
- yield value;
491
+ for (const value of elements) { yield value; }
148
492
  }
493
+ else { yield elements; }
149
494
 
150
495
  index += 1;
151
496
  }
152
497
  });
153
498
  }
154
499
 
500
+ /**
501
+ * Drops a given number of elements at the beginning of the iterator.
502
+ * The remaining elements will be included in a new iterator.
503
+ * See also {@link SmartIterator.take}.
504
+ *
505
+ * Since the iterator is lazy, the dropping process will
506
+ * be executed once the resulting iterator is materialized.
507
+ *
508
+ * A new iterator will be created, holding the reference to the original one.
509
+ * This means that the original iterator won't be consumed until the
510
+ * new one is and that consuming one of them will consume the other as well.
511
+ *
512
+ * Only the dropped elements will be consumed in the process.
513
+ * The rest of the iterator will be consumed only once the new one is.
514
+ *
515
+ * ---
516
+ *
517
+ * @example
518
+ * ```ts
519
+ * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
520
+ * const result = iterator.drop(3);
521
+ *
522
+ * console.log(result.toArray()); // [1, 2]
523
+ * ```
524
+ *
525
+ * ---
526
+ *
527
+ * @param count The number of elements to drop.
528
+ *
529
+ * @returns A new {@link SmartIterator} containing the remaining elements.
530
+ */
155
531
  public drop(count: number): SmartIterator<T, R | undefined>
156
532
  {
157
533
  const iterator = this._iterator;
@@ -176,6 +552,39 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
176
552
  }
177
553
  });
178
554
  }
555
+
556
+ /**
557
+ * Takes a given number of elements at the beginning of the iterator.
558
+ * These elements will be included in a new iterator.
559
+ * See also {@link SmartIterator.drop}.
560
+ *
561
+ * Since the iterator is lazy, the taking process will
562
+ * be executed once the resulting iterator is materialized.
563
+ *
564
+ * A new iterator will be created, holding the reference to the original one.
565
+ * This means that the original iterator won't be consumed until the
566
+ * new one is and that consuming one of them will consume the other as well.
567
+ *
568
+ * Only the taken elements will be consumed from the original iterator.
569
+ * The rest of the original iterator will be available for further consumption.
570
+ *
571
+ * ---
572
+ *
573
+ * @example
574
+ * ```ts
575
+ * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
576
+ * const result = iterator.take(3);
577
+ *
578
+ * console.log(result.toArray()); // [-2, -1, 0]
579
+ * console.log(iterator.toArray()); // [1, 2]
580
+ * ```
581
+ *
582
+ * ---
583
+ *
584
+ * @param limit The number of elements to take.
585
+ *
586
+ * @returns A new {@link SmartIterator} containing the taken elements.
587
+ */
179
588
  public take(limit: number): SmartIterator<T, R | undefined>
180
589
  {
181
590
  const iterator = this._iterator;
@@ -197,8 +606,75 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
197
606
  });
198
607
  }
199
608
 
609
+ /**
610
+ * Finds the first element of the iterator that satisfies a given condition.
611
+ *
612
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
613
+ * The first element that satisfies the condition will be returned immediately.
614
+ *
615
+ * Only the elements that are necessary to find the first
616
+ * satisfying one will be consumed from the original iterator.
617
+ * The rest of the original iterator will be available for further consumption.
618
+ *
619
+ * Also note that:
620
+ * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed.
621
+ * - If the iterator is infinite and no element satisfies the condition, the method will never return.
622
+ *
623
+ * ---
624
+ *
625
+ * @example
626
+ * ```ts
627
+ * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
628
+ * const result = iterator.find((value) => value > 0);
629
+ *
630
+ * console.log(result); // 1
631
+ * ```
632
+ *
633
+ * ---
634
+ *
635
+ * @param predicate The condition to check for each element of the iterator.
636
+ *
637
+ * @returns The first element that satisfies the condition, `undefined` otherwise.
638
+ */
200
639
  public find(predicate: Iteratee<T, boolean>): T | undefined;
201
- public find<S extends T>(predicate: TypeGuardIteratee<T, S>): S | undefined;
640
+
641
+ /**
642
+ * Finds the first element of the iterator that satisfies a given condition.
643
+ *
644
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
645
+ * The first element that satisfies the condition will be returned immediately.
646
+ *
647
+ * Only the elements that are necessary to find the first
648
+ * satisfying one will be consumed from the original iterator.
649
+ * The rest of the original iterator will be available for further consumption.
650
+ *
651
+ * Also note that:
652
+ * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed.
653
+ * - If the iterator is infinite and no element satisfies the condition, the method will never return.
654
+ *
655
+ * ---
656
+ *
657
+ * @example
658
+ * ```ts
659
+ * const iterator = new SmartIterator<number | string>([-2, "-1", "0", 1, "2"]);
660
+ * const result = iterator.find<number>((value) => typeof value === "number");
661
+ *
662
+ * console.log(result); // -2
663
+ * ```
664
+ *
665
+ * ---
666
+ *
667
+ * @template S
668
+ * The type of the element that satisfies the condition.
669
+ * This allows the type-system to infer the correct type of the result.
670
+ *
671
+ * It must be a subtype of the original type of the elements.
672
+ *
673
+ * @param predicate The type guard condition to check for each element of the iterator.
674
+ *
675
+ * @returns The first element that satisfies the condition, `undefined` otherwise.
676
+ */
677
+ public find<S extends T>(predicate: TypeGuardPredicate<T, S>): S | undefined;
202
678
  public find(predicate: Iteratee<T, boolean>): T | undefined
203
679
  {
204
680
  let index = 0;
@@ -214,10 +690,61 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
214
690
  }
215
691
  }
216
692
 
693
+ /**
694
+ * Enumerates the elements of the iterator.
695
+ * Each element is be paired with its index in a new iterator.
696
+ *
697
+ * Since the iterator is lazy, the enumeration process will
698
+ * be executed once the resulting iterator is materialized.
699
+ *
700
+ * A new iterator will be created, holding the reference to the original one.
701
+ * This means that the original iterator won't be consumed until the
702
+ * new one is and that consuming one of them will consume the other as well.
703
+ *
704
+ * ---
705
+ *
706
+ * @example
707
+ * ```ts
708
+ * const iterator = new SmartIterator<string>(["A", "M", "N", "Z"]);
709
+ * const result = iterator.enumerate();
710
+ *
711
+ * console.log(result.toArray()); // [[0, "A"], [1, "M"], [2, "N"], [3, "Z"]]
712
+ * ```
713
+ *
714
+ * ---
715
+ *
716
+ * @returns A new {@link SmartIterator} containing the enumerated elements.
717
+ */
217
718
  public enumerate(): SmartIterator<[number, T], R>
218
719
  {
219
720
  return this.map((value, index) => [index, value]);
220
721
  }
722
+
723
+ /**
724
+ * Removes all duplicate elements from the iterator.
725
+ * The first occurrence of each element will be kept.
726
+ *
727
+ * Since the iterator is lazy, the deduplication process will
728
+ * be executed once the resulting iterator is materialized.
729
+ *
730
+ * A new iterator will be created, holding the reference to the original one.
731
+ * This means that the original iterator won't be consumed until the
732
+ * new one is and that consuming one of them will consume the other as well.
733
+ *
734
+ * ---
735
+ *
736
+ * @example
737
+ * ```ts
738
+ * const iterator = new SmartIterator<number>([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]);
739
+ * const result = iterator.unique();
740
+ *
741
+ * console.log(result.toArray()); // [1, 2, 3, 4, 5]
742
+ * ```
743
+ *
744
+ * ---
745
+ *
746
+ * @returns A new {@link SmartIterator} containing only the unique elements.
747
+ */
221
748
  public unique(): SmartIterator<T, R>
222
749
  {
223
750
  const iterator = this._iterator;
@@ -225,14 +752,11 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
225
752
  return new SmartIterator<T, R>(function* ()
226
753
  {
227
754
  const values = new Set<T>();
228
-
229
755
  while (true)
230
756
  {
231
757
  const result = iterator.next();
232
-
233
758
  if (result.done) { return result.value; }
234
759
  if (values.has(result.value)) { continue; }
235
-
236
760
  values.add(result.value);
237
761
 
238
762
  yield result.value;
@@ -240,6 +764,26 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
240
764
  });
241
765
  }
242
766
 
767
+ /**
768
+ * Counts the number of elements in the iterator.
769
+ * This method will consume the entire iterator in the process.
770
+ *
771
+ * If the iterator is infinite, the method will never return.
772
+ *
773
+ * ---
774
+ *
775
+ * @example
776
+ * ```ts
777
+ * const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);
778
+ * const result = iterator.count();
779
+ *
780
+ * console.log(result); // 5
781
+ * ```
782
+ *
783
+ * ---
784
+ *
785
+ * @returns The number of elements in the iterator.
786
+ */
243
787
  public count(): number
244
788
  {
245
789
  let index = 0;
@@ -253,6 +797,28 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
253
797
  }
254
798
  }
255
799
 
800
+ /**
801
+ * Iterates over all elements of the iterator.
802
+ * The elements are passed to the function along with their index.
803
+ *
804
+ * This method will consume the entire iterator in the process.
805
+ * If the iterator is infinite, the method will never return.
806
+ *
807
+ * ---
808
+ *
809
+ * @example
810
+ * ```ts
811
+ * const iterator = new SmartIterator<number>(["A", "M", "N", "Z"]);
812
+ * iterator.forEach((value, index) =>
813
+ * {
814
+ * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z"
815
+ * }
816
+ * ```
817
+ *
818
+ * ---
819
+ *
820
+ * @param iteratee The function to apply to each element of the iterator.
821
+ */
256
822
  public forEach(iteratee: Iteratee<T>): void
257
823
  {
258
824
  let index = 0;
@@ -268,11 +834,157 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
268
834
  }
269
835
  }
270
836
 
837
+ /**
838
+ * Advances the iterator to the next element and returns the result.
839
+ * If the iterator requires it, a value must be provided to be passed to the next element.
840
+ *
841
+ * Once the iterator is done, the method will return an object with the `done` property set to `true`.
842
+ *
843
+ * ---
844
+ *
845
+ * @example
846
+ * ```ts
847
+ * const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);
848
+ *
849
+ * let result = iterator.next();
850
+ * while (!result.done)
851
+ * {
852
+ * console.log(result.value); // 1, 2, 3, 4, 5
853
+ *
854
+ * result = iterator.next();
855
+ * }
856
+ *
857
+ * console.log(result); // { done: true, value: undefined }
858
+ * ```
859
+ *
860
+ * ---
861
+ *
862
+ * @param values The value to pass to the next element, if required.
863
+ *
864
+ * @returns The result of the iteration, containing the value of the operation.
865
+ */
271
866
  public next(...values: N extends undefined ? [] : [N]): IteratorResult<T, R>
272
867
  {
273
868
  return this._iterator.next(...values);
274
869
  }
275
870
 
871
+ /**
872
+ * An utility method that may be used to close the iterator gracefully,
873
+ * free the resources and perform any cleanup operation.
874
+ * It may also be used to signal the end or to compute a specific final result of the iteration process.
875
+ *
876
+ * ---
877
+ *
878
+ * @example
879
+ * ```ts
880
+ * const iterator = new SmartIterator<number>({
881
+ * _index: 0,
882
+ * next: function()
883
+ * {
884
+ * return { done: false, value: this._index += 1 };
885
+ * },
886
+ * return: function() { console.log("Closing the iterator..."); }
887
+ * });
888
+ *
889
+ * for (const value of iterator)
890
+ * {
891
+ * if (value > 5) { break; } // Closing the iterator...
892
+ *
893
+ * console.log(value); // 1, 2, 3, 4, 5
894
+ * }
895
+ * ```
896
+ *
897
+ * ---
898
+ *
899
+ * @param value The final value of the iterator.
900
+ *
901
+ * @returns The result of the iterator.
902
+ */
903
+ public return(value?: R): IteratorResult<T, R>
904
+ {
905
+ if (this._iterator.return) { return this._iterator.return(value); }
906
+
907
+ return { done: true, value: value as R };
908
+ }
909
+
910
+ /**
911
+ * An utility method that may be used to close the iterator due to an error,
912
+ * free the resources and perform any cleanup operation.
913
+ * It may also be used to signal that an error occurred during the iteration process or to handle it.
914
+ *
915
+ * ---
916
+ *
917
+ * @example
918
+ * ```ts
919
+ * const iterator = new SmartIterator<number>({
920
+ * _index: 0,
921
+ * next: function()
922
+ * {
923
+ * return { done: this._index > 10, value: this._index += 1 };
924
+ * },
925
+ * throw: function(error)
926
+ * {
927
+ * console.warn(error.message);
928
+ *
929
+ * this._index = 0;
930
+ * }
931
+ * });
932
+ *
933
+ * for (const value of iterator) // 1, 2, 3, 4, 5, "The index is too high.", 1, 2, 3, 4, 5, ...
934
+ * {
935
+ * try
936
+ * {
937
+ * if (value > 5) { throw new Error("The index is too high."); }
938
+ *
939
+ * console.log(value); // 1, 2, 3, 4, 5
940
+ * }
941
+ * catch (error) { iterator.throw(error); }
942
+ * }
943
+ * ```
944
+ *
945
+ * ---
946
+ *
947
+ * @param error The error to throw into the iterator.
948
+ *
949
+ * @returns The final result of the iterator.
950
+ */
951
+ public throw(error: unknown): IteratorResult<T, R>
952
+ {
953
+ if (this._iterator.throw) { return this._iterator.throw(error); }
954
+
955
+ throw error;
956
+ }
957
+
958
+ /**
959
+ * An utility method that aggregates the elements of the iterator using a given key function.
960
+ * The elements will be grouped by the resulting keys in a new specialized iterator.
961
+ * See {@link AggregatedIterator}.
962
+ *
963
+ * Since the iterator is lazy, the grouping process will
964
+ * be executed once the resulting iterator is materialized.
965
+ *
966
+ * A new iterator will be created, holding the reference to the original one.
967
+ * This means that the original iterator won't be consumed until the
968
+ * the new one is and that consuming one of them will consume the other as well.
969
+ *
970
+ * ---
971
+ *
972
+ * @example
973
+ * ```ts
974
+ * const iterator = new SmartIterator<number>([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
975
+ * const result = iterator.groupBy<string>((value) => value % 2 === 0 ? "even" : "odd");
976
+ *
977
+ * console.log(result.toObject()); // { odd: [1, 3, 5, 7, 9], even: [2, 4, 6, 8, 10] }
978
+ * ```
979
+ *
980
+ * ---
981
+ *
982
+ * @template K The type of the keys used to group the elements.
983
+ *
984
+ * @param iteratee The key function to apply to each element of the iterator.
985
+ *
986
+ * @returns A new instance of the {@link AggregatedIterator} class containing the grouped elements.
987
+ */
276
988
  public groupBy<K extends PropertyKey>(iteratee: Iteratee<T, K>): AggregatedIterator<K, T>
277
989
  {
278
990
  return new AggregatedIterator(this.map((element, index) =>
@@ -283,6 +995,29 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
283
995
  }));
284
996
  }
285
997
 
998
+ /**
999
+ * Materializes the iterator into an array.
1000
+ * This method will consume the entire iterator in the process.
1001
+ *
1002
+ * If the iterator is infinite, the method will never return.
1003
+ *
1004
+ * ---
1005
+ *
1006
+ * @example
1007
+ * ```ts
1008
+ * const iterator = new SmartIterator(function* ()
1009
+ * {
1010
+ * for (let i = 0; i < 5; i += 1) { yield i; }
1011
+ * });
1012
+ * const result = iterator.toArray();
1013
+ *
1014
+ * console.log(result); // [0, 1, 2, 3, 4]
1015
+ * ```
1016
+ *
1017
+ * ---
1018
+ *
1019
+ * @returns The {@link Array} containing all elements of the iterator.
1020
+ */
286
1021
  public toArray(): T[]
287
1022
  {
288
1023
  return Array.from(this as Iterable<T>);