@byloth/core 2.0.0-rc.9 → 2.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.
Files changed (47) hide show
  1. package/dist/core.js +3371 -608
  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 +13 -10
  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 +765 -21
  10. package/src/models/aggregators/aggregated-iterator.ts +698 -22
  11. package/src/models/aggregators/reduced-iterator.ts +699 -10
  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 +139 -4
  16. package/src/models/callbacks/switchable-callback.ts +138 -4
  17. package/src/models/callbacks/types.ts +16 -0
  18. package/src/models/exceptions/core.ts +112 -3
  19. package/src/models/exceptions/index.ts +340 -13
  20. package/src/models/index.ts +4 -8
  21. package/src/models/iterators/smart-async-iterator.ts +687 -22
  22. package/src/models/iterators/smart-iterator.ts +631 -21
  23. package/src/models/iterators/types.ts +268 -9
  24. package/src/models/json/json-storage.ts +388 -110
  25. package/src/models/json/types.ts +10 -1
  26. package/src/models/promises/deferred-promise.ts +75 -5
  27. package/src/models/promises/index.ts +1 -3
  28. package/src/models/promises/smart-promise.ts +232 -4
  29. package/src/models/promises/timed-promise.ts +38 -1
  30. package/src/models/promises/types.ts +84 -2
  31. package/src/models/timers/clock.ts +91 -19
  32. package/src/models/timers/countdown.ts +152 -22
  33. package/src/models/timers/game-loop.ts +243 -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 +75 -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 +109 -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
@@ -3,21 +3,136 @@ import { SmartIterator } from "../iterators/index.js";
3
3
  import type { GeneratorFunction } from "../iterators/types.js";
4
4
 
5
5
  import AggregatedIterator from "./aggregated-iterator.js";
6
- import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardIteratee } from "./types.js";
7
-
6
+ import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardPredicate } from "./types.js";
7
+
8
+ /**
9
+ * A class representing an aggregated iterator that has been reduced in a lazy and optimized way.
10
+ *
11
+ * It's part of the {@link AggregatedIterator} and {@link AggregatedAsyncIterator} implementations,
12
+ * providing a way to reduce them into a single value or another aggregated iterable.
13
+ * For this reason, it isn't recommended to instantiate this class directly
14
+ * (although it's still possible), but rather use the reducing methods provided by the aggregated iterators.
15
+ *
16
+ * It isn't directly iterable, just like its parent class, and needs to specify on what you want to iterate.
17
+ * See the {@link ReducedIterator.keys}, {@link ReducedIterator.entries}
18
+ * & {@link ReducedIterator.values} methods.
19
+ * It does, however, provide the same set of methods to perform
20
+ * operations and transformation on the elements of the iterator,
21
+ * having also the knowledge and context of the groups to which
22
+ * they belong, allowing to handle them in a grouped manner.
23
+ *
24
+ * This is particularly useful when you have group elements and
25
+ * need perform specific operations on the reduced elements.
26
+ *
27
+ * ```ts
28
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
29
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
30
+ * .count();
31
+ *
32
+ * console.log(results.toObject()); // { odd: 4, even: 4 }
33
+ * ```
34
+ *
35
+ * @template K The type of the key used to group the elements.
36
+ * @template T The type of the elements in the iterator.
37
+ */
8
38
  export default class ReducedIterator<K extends PropertyKey, T>
9
39
  {
40
+ /**
41
+ * The internal {@link SmartIterator} object that holds the reduced elements.
42
+ */
10
43
  protected _elements: SmartIterator<[K, T]>;
11
44
 
45
+ /**
46
+ * Initializes a new instance of the {@link ReducedIterator} class.
47
+ *
48
+ * ```ts
49
+ * const results = new ReducedIterator<string, number>([["A", 1], ["B", 2], ["C", 4]]);
50
+ * ```
51
+ *
52
+ * @param iterable A reduced iterable object.
53
+ */
12
54
  public constructor(iterable: Iterable<[K, T]>);
55
+
56
+ /**
57
+ * Initializes a new instance of the {@link ReducedIterator} class.
58
+ *
59
+ * ```ts
60
+ * const results = new ReducedIterator<string, number>({
61
+ * _index: 0,
62
+ * next: () =>
63
+ * {
64
+ * if (this._index >= 3) { return { done: true, value: undefined }; }
65
+ * this._index += 1;
66
+ *
67
+ * return { done: false, value: [["A", "B", "C"][this._index], (this._index + 1)] };
68
+ * }
69
+ * });
70
+ * ```
71
+ *
72
+ * @param iterator An reduced iterator object.
73
+ */
13
74
  public constructor(iterator: Iterator<[K, T]>);
75
+
76
+ /**
77
+ * Initializes a new instance of the {@link ReducedIterator} class.
78
+ *
79
+ * ```ts
80
+ * import { range, Random } from "@byloth/core";
81
+ *
82
+ * const results = new ReducedIterator<string, number>(function* ()
83
+ * {
84
+ * for (const index of range(3))
85
+ * {
86
+ * yield [["A", "B", "C"][index], (index + 1)];
87
+ * }
88
+ * });
89
+ * ```
90
+ *
91
+ * @param generatorFn A generator function that produces the reduced elements.
92
+ */
14
93
  public constructor(generatorFn: GeneratorFunction<[K, T]>);
94
+
95
+ /**
96
+ * Initializes a new instance of the {@link ReducedIterator} class.
97
+ *
98
+ * ```ts
99
+ * const results = new ReducedIterator(reducedValues);
100
+ * ```
101
+ *
102
+ * @param argument An iterable, iterator or generator function that produces the reduced elements.
103
+ */
15
104
  public constructor(argument: Iterable<[K, T]> | Iterator<[K, T]> | GeneratorFunction<[K, T]>);
16
105
  public constructor(argument: Iterable<[K, T]> | Iterator<[K, T]> | GeneratorFunction<[K, T]>)
17
106
  {
18
107
  this._elements = new SmartIterator(argument);
19
108
  }
20
109
 
110
+ /**
111
+ * Determines whether all elements of the reduced iterator satisfy the given condition.
112
+ * See also {@link ReducedIterator.some}.
113
+ *
114
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
115
+ * Once a single element doesn't satisfy the condition, the method will return `false` immediately.
116
+ *
117
+ * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
118
+ * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
119
+ * Consider using {@link ReducedIterator.find} instead.
120
+ *
121
+ * If the iterator is infinite and every element satisfies the condition, the method will never return.
122
+ *
123
+ * ```ts
124
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
125
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
126
+ * .reduce((key, accumulator, value) => accumulator + value)
127
+ * .every((key, value) => value > 0);
128
+ *
129
+ * console.log(results); // true
130
+ * ```
131
+ *
132
+ * @param predicate The condition to check for each element of the iterator.
133
+ *
134
+ * @returns `true` if all elements satisfy the condition, `false` otherwise.
135
+ */
21
136
  public every(predicate: KeyedIteratee<K, T, boolean>): boolean
22
137
  {
23
138
  for (const [index, [key, element]] of this._elements.enumerate())
@@ -27,6 +142,33 @@ export default class ReducedIterator<K extends PropertyKey, T>
27
142
 
28
143
  return true;
29
144
  }
145
+
146
+ /**
147
+ * Determines whether any element of the reduced iterator satisfies the given condition.
148
+ * See also {@link ReducedIterator.every}.
149
+ *
150
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
151
+ * Once a single element satisfies the condition, the method will return `true` immediately.
152
+ *
153
+ * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
154
+ * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
155
+ * Consider using {@link ReducedIterator.find} instead.
156
+ *
157
+ * If the iterator is infinite and no element satisfies the condition, the method will never return.
158
+ *
159
+ * ```ts
160
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
161
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
162
+ * .reduce((key, accumulator, value) => accumulator + value)
163
+ * .some((key, value) => value > 0);
164
+ *
165
+ * console.log(results); // true
166
+ * ```
167
+ *
168
+ * @param predicate The condition to check for each element of the iterator.
169
+ *
170
+ * @returns `true` if any element satisfies the condition, `false` otherwise.
171
+ */
30
172
  public some(predicate: KeyedIteratee<K, T, boolean>): boolean
31
173
  {
32
174
  for (const [index, [key, element]] of this._elements.enumerate())
@@ -37,8 +179,67 @@ export default class ReducedIterator<K extends PropertyKey, T>
37
179
  return false;
38
180
  }
39
181
 
182
+ /**
183
+ * Filters the elements of the reduced iterator using a given condition.
184
+ *
185
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
186
+ * If the condition is met, the element will be included in the new iterator.
187
+ *
188
+ * Since the iterator is lazy, the filtering process will
189
+ * be executed once the resulting iterator is materialized.
190
+ *
191
+ * A new iterator will be created, holding the reference to the original one.
192
+ * This means that the original iterator won't be consumed until the
193
+ * new one is and that consuming one of them will consume the other as well.
194
+ *
195
+ * ```ts
196
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
197
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
198
+ * .reduce((key, accumulator, value) => accumulator + value)
199
+ * .filter((key, value) => value > 0);
200
+ *
201
+ * console.log(results.toObject()); // { odd: 4, even: 16 }
202
+ * ```
203
+ *
204
+ * @param predicate The condition to check for each element of the iterator.
205
+ *
206
+ * @returns A new {@link ReducedIterator} containing only the elements that satisfy the condition.
207
+ */
40
208
  public filter(predicate: KeyedIteratee<K, T, boolean>): ReducedIterator<K, T>;
41
- public filter<S extends T>(predicate: KeyedTypeGuardIteratee<K, T, S>): ReducedIterator<K, S>;
209
+
210
+ /**
211
+ * Filters the elements of the reduced iterator using a given type guard predicate.
212
+ *
213
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
214
+ * If the condition is met, the element will be included in the new iterator.
215
+ *
216
+ * Since the iterator is lazy, the filtering process will
217
+ * be executed once the resulting iterator is materialized.
218
+ *
219
+ * A new iterator will be created, holding the reference to the original one.
220
+ * This means that the original iterator won't be consumed until the
221
+ * new one is and that consuming one of them will consume the other as well.
222
+ *
223
+ * ```ts
224
+ * const results = new SmartIterator<number | string>([-3, -1, "0", "2", 3, 5, "6", "8"])
225
+ * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd")
226
+ * .reduce((key, accumulator, value) => accumulator + value)
227
+ * .filter<number>((key, value) => typeof value === "number");
228
+ *
229
+ * console.log(results.toObject()); // { odd: 4 }
230
+ * ```
231
+ *
232
+ * @template S
233
+ * The type of the elements that satisfy the condition.
234
+ * This allows the type-system to infer the correct type of the iterator.
235
+ *
236
+ * It must be a subtype of the original type of the elements.
237
+ *
238
+ * @param predicate The type guard condition to check for each element of the iterator.
239
+ *
240
+ * @returns A new {@link ReducedIterator} containing only the elements that satisfy the condition.
241
+ */
242
+ public filter<S extends T>(predicate: KeyedTypeGuardPredicate<K, T, S>): ReducedIterator<K, S>;
42
243
  public filter(predicate: KeyedIteratee<K, T, boolean>): ReducedIterator<K, T>
43
244
  {
44
245
  const elements = this._elements.enumerate();
@@ -51,6 +252,35 @@ export default class ReducedIterator<K extends PropertyKey, T>
51
252
  }
52
253
  });
53
254
  }
255
+
256
+ /**
257
+ * Maps the elements of the reduced iterator using a given transformation function.
258
+ *
259
+ * This method will iterate over all the elements of the iterator applying the transformation function.
260
+ * The result of the transformation will be included in the new iterator.
261
+ *
262
+ * Since the iterator is lazy, the mapping process will
263
+ * be executed once the resulting iterator is materialized.
264
+ *
265
+ * A new iterator will be created, holding the reference to the original one.
266
+ * This means that the original iterator won't be consumed until the
267
+ * new one is and that consuming one of them will consume the other as well.
268
+ *
269
+ * ```ts
270
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
271
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
272
+ * .reduce((key, accumulator, value) => accumulator + value)
273
+ * .map((key, value) => value * 2);
274
+ *
275
+ * console.log(results.toObject()); // { odd: 8, even: 32 }
276
+ * ```
277
+ *
278
+ * @template V The type of the elements after the transformation.
279
+ *
280
+ * @param iteratee The transformation function to apply to each element of the iterator.
281
+ *
282
+ * @returns A new {@link ReducedIterator} containing the transformed elements.
283
+ */
54
284
  public map<V>(iteratee: KeyedIteratee<K, T, V>): ReducedIterator<K, V>
55
285
  {
56
286
  const elements = this._elements.enumerate();
@@ -63,7 +293,64 @@ export default class ReducedIterator<K extends PropertyKey, T>
63
293
  }
64
294
  });
65
295
  }
296
+
297
+ /**
298
+ * Reduces the elements of the reduced iterator using a given reducer function.
299
+ * This method will consume the entire iterator in the process.
300
+ *
301
+ * It will iterate over all the elements of the iterator applying the reducer function.
302
+ * The result of each iteration will be passed as the accumulator to the next one.
303
+ *
304
+ * The first accumulator value will be the first element of the iterator.
305
+ * The last accumulator value will be the final result of the reduction.
306
+ *
307
+ * Also note that:
308
+ * - If an empty iterator is provided, a {@link ValueException} will be thrown.
309
+ * - If the iterator is infinite, the method will never return.
310
+ *
311
+ * ```ts
312
+ * const result = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
313
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
314
+ * .reduce((key, accumulator, value) => accumulator + value)
315
+ * .reduce((key, accumulator, value) => accumulator + value);
316
+ *
317
+ * console.log(result); // 20
318
+ * ```
319
+ *
320
+ * @param reducer The reducer function to apply to the elements of the iterator.
321
+ *
322
+ * @returns The final value after reducing all the elements of the iterator.
323
+ */
66
324
  public reduce(reducer: KeyedReducer<K, T, T>): T;
325
+
326
+ /**
327
+ * Reduces the elements of the reduced iterator using a given reducer function.
328
+ * This method will consume the entire iterator in the process.
329
+ *
330
+ * It will iterate over all the elements of the iterator applying the reducer function.
331
+ * The result of each iteration will be passed as the accumulator to the next one.
332
+ *
333
+ * The first accumulator value will be the provided initial value.
334
+ * The last accumulator value will be the final result of the reduction.
335
+ *
336
+ * If the iterator is infinite, the method will never return.
337
+ *
338
+ * ```ts
339
+ * const result = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
340
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
341
+ * .reduce((key, accumulator, value) => accumulator + value)
342
+ * .reduce((key, { value }, currentValue) => ({ value: value + currentValue }), { value: 0 });
343
+ *
344
+ * console.log(result); // { value: 20 }
345
+ * ```
346
+ *
347
+ * @template A The type of the accumulator value which will also be the type of the final result of the reduction.
348
+ *
349
+ * @param reducer The reducer function to apply to the elements of the iterator.
350
+ * @param initialValue The initial value of the accumulator.
351
+ *
352
+ * @returns The final result of the reduction.
353
+ */
67
354
  public reduce<A>(reducer: KeyedReducer<K, T, A>, initialValue: A): A;
68
355
  public reduce<A>(reducer: KeyedReducer<K, T, A>, initialValue?: A): A
69
356
  {
@@ -88,7 +375,35 @@ export default class ReducedIterator<K extends PropertyKey, T>
88
375
  return accumulator;
89
376
  }
90
377
 
91
- public flatMap<V>(iteratee: KeyedIteratee<K, T, Iterable<V>>): AggregatedIterator<K, V>
378
+ /**
379
+ * Flattens the elements of the reduced iterator using a given transformation function.
380
+ *
381
+ * This method will iterate over all the elements of the iterator applying the transformation function.
382
+ * The result of each transformation will be flattened into the new iterator.
383
+ *
384
+ * Since the iterator is lazy, the flattening process will
385
+ * be executed once the resulting iterator is materialized.
386
+ *
387
+ * A new iterator will be created, holding the reference to the original one.
388
+ * This means that the original iterator won't be consumed until the
389
+ * new one is and that consuming one of them will consume the other as well.
390
+ *
391
+ * ```ts
392
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
393
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
394
+ * .reduce((key, accumulator, value) => accumulator.concat([value]), () => [])
395
+ * .flatMap((key, value) => value);
396
+ *
397
+ * console.log(results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] }
398
+ * ```
399
+ *
400
+ * @template V The type of the elements after the transformation.
401
+ *
402
+ * @param iteratee The transformation function to apply to each element of the iterator.
403
+ *
404
+ * @returns A new {@link AggregatedIterator} containing the flattened elements.
405
+ */
406
+ public flatMap<V>(iteratee: KeyedIteratee<K, T, V | readonly V[]>): AggregatedIterator<K, V>
92
407
  {
93
408
  const elements = this._elements.enumerate();
94
409
 
@@ -96,11 +411,45 @@ export default class ReducedIterator<K extends PropertyKey, T>
96
411
  {
97
412
  for (const [index, [key, element]] of elements)
98
413
  {
99
- for (const value of iteratee(key, element, index)) { yield [key, value]; }
414
+ const values = iteratee(key, element, index);
415
+
416
+ if (values instanceof Array)
417
+ {
418
+ for (const value of values) { yield [key, value]; }
419
+ }
420
+ else { yield [key, values]; }
100
421
  }
101
422
  });
102
423
  }
103
424
 
425
+ /**
426
+ * Drops a given number of elements at the beginning of the reduced iterator.
427
+ * The remaining elements will be included in the new iterator.
428
+ * See also {@link ReducedIterator.take}.
429
+ *
430
+ * Since the iterator is lazy, the dropping process will
431
+ * be executed once the resulting iterator is materialized.
432
+ *
433
+ * A new iterator will be created, holding the reference to the original one.
434
+ * This means that the original iterator won't be consumed until the
435
+ * new one is and that consuming one of them will consume the other as well.
436
+ *
437
+ * Only the dropped elements will be consumed in the process.
438
+ * The rest of the iterator will be consumed once the new iterator is.
439
+ *
440
+ * ```ts
441
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
442
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
443
+ * .reduce((key, accumulator, value) => accumulator.concat(value), () => [])
444
+ * .drop(1);
445
+ *
446
+ * console.log(results.toObject()); // { even: [0, 2, 6, 8] }
447
+ * ```
448
+ *
449
+ * @param count The number of elements to drop.
450
+ *
451
+ * @returns A new {@link ReducedIterator} containing the remaining elements.
452
+ */
104
453
  public drop(count: number): ReducedIterator<K, T>
105
454
  {
106
455
  const elements = this._elements.enumerate();
@@ -113,6 +462,37 @@ export default class ReducedIterator<K extends PropertyKey, T>
113
462
  }
114
463
  });
115
464
  }
465
+
466
+ /**
467
+ * Takes a given number of elements at the beginning of the reduced iterator.
468
+ * The elements will be included in the new iterator.
469
+ * See also {@link ReducedIterator.drop}.
470
+ *
471
+ * Since the iterator is lazy, the taking process will
472
+ * be executed once the resulting iterator is materialized.
473
+ *
474
+ * A new iterator will be created, holding the reference to the original one.
475
+ * This means that the original iterator won't be consumed until the
476
+ * new one is and that consuming one of them will consume the other as well.
477
+ *
478
+ * Only the taken elements will be consumed from the original reduced iterator.
479
+ * The rest of the original reduced iterator will be available for further consumption.
480
+ *
481
+ * ```ts
482
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
483
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
484
+ * .reduce((key, accumulator, value) => accumulator.concat(value), () => []);
485
+ *
486
+ * const results = iterator.take(1);
487
+ *
488
+ * console.log(results.toObject()); // { odd: [-3, -1, 3, 5] }
489
+ * console.log(reduced.toObject()); // { even: [0, 2, 6, 8] }
490
+ * ```
491
+ *
492
+ * @param count The number of elements to take.
493
+ *
494
+ * @returns A new {@link ReducedIterator} containing the taken elements.
495
+ */
116
496
  public take(count: number): ReducedIterator<K, T>
117
497
  {
118
498
  const elements = this._elements.enumerate();
@@ -122,16 +502,131 @@ export default class ReducedIterator<K extends PropertyKey, T>
122
502
  for (const [index, [key, element]] of elements)
123
503
  {
124
504
  if (index >= count) { break; }
125
-
126
505
  yield [key, element];
127
506
  }
128
507
  });
129
508
  }
130
509
 
510
+ /**
511
+ * Finds the first element of the reduced iterator that satisfies the given condition.
512
+ *
513
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
514
+ * The first element that satisfies the condition will be returned immediately.
515
+ *
516
+ * Only the elements that are necessary to find the first
517
+ * satisfying one will be consumed from the original iterator.
518
+ * The rest of the iterator will be available for further consumption.
519
+ *
520
+ * Also note that:
521
+ * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed.
522
+ * - If the iterator is infinite and no element satisfies the condition, the method will never return.
523
+ *
524
+ * ```ts
525
+ * const results = new SmartIterator<number>([-3, -3, -1, 0, 1, 2, 5, 6, 8])
526
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
527
+ * .reduce((key, accumulator, value) => accumulator + value)
528
+ * .find((key, value) => value > 0);
529
+ *
530
+ * console.log(results); // 16
531
+ *
532
+ * @param predicate The condition to check for each element of the iterator.
533
+ *
534
+ * @returns The first element that satisfies the condition, `undefined` otherwise.
535
+ */
536
+ public find(predicate: KeyedIteratee<K, T, boolean>): T | undefined;
537
+
538
+ /**
539
+ * Finds the first element of the reduced iterator that satisfies the given type guard predicate.
540
+ *
541
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
542
+ * The first element that satisfies the condition will be returned immediately.
543
+ *
544
+ * Only the elements that are necessary to find the first
545
+ * satisfying one will be consumed from the original iterator.
546
+ * The rest of the iterator will be available for further consumption.
547
+ *
548
+ * Also note that:
549
+ * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed.
550
+ * - If the iterator is infinite and no element satisfies the condition, the method will never return.
551
+ *
552
+ * ```ts
553
+ * const results = new SmartIterator<number | string>(["-3", -3, "-1", 0, 1, 2, "5", 6, 8])
554
+ * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd")
555
+ * .reduce((key, accumulator, value) => accumulator + value)
556
+ * .find<number>((key, value) => typeof value === "number");
557
+ *
558
+ * console.log(results); // 16
559
+ *
560
+ * @template S
561
+ * The type of the elements that satisfy the condition.
562
+ * This allows the type-system to infer the correct type of the result.
563
+ *
564
+ * It must be a subtype of the original type of the elements.
565
+ *
566
+ * @param predicate The type guard condition to check for each element of the iterator.
567
+ *
568
+ * @returns The first element that satisfies the condition, `undefined` otherwise.
569
+ */
570
+ public find<S extends T>(predicate: KeyedTypeGuardPredicate<K, T, S>): S | undefined;
571
+ public find(predicate: KeyedIteratee<K, T, boolean>): T | undefined
572
+ {
573
+ for (const [index, [key, element]] of this._elements.enumerate())
574
+ {
575
+ if (predicate(key, element, index)) { return element; }
576
+ }
577
+
578
+ return undefined;
579
+ }
580
+
581
+ /**
582
+ * Enumerates the elements of the reduced iterator.
583
+ * Each element is paired with its index in a new iterator.
584
+ *
585
+ * Since the iterator is lazy, the enumeration process will
586
+ * be executed once the resulting iterator is materialized.
587
+ *
588
+ * A new iterator will be created, holding the reference to the original one.
589
+ * This means that the original iterator won't be consumed until the
590
+ * new one is and that consuming one of them will consume the other as well.
591
+ *
592
+ * ```ts
593
+ * const results = new ReducedIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
594
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
595
+ * .reduce((key, accumulator, value) => accumulator + value)
596
+ * .enumerate();
597
+ *
598
+ * console.log(results.toObject()); // [[0, 4], [1, 16]]
599
+ * ```
600
+ *
601
+ * @returns A new {@link ReducedIterator} object containing the enumerated elements.
602
+ */
131
603
  public enumerate(): ReducedIterator<K, [number, T]>
132
604
  {
133
605
  return this.map((_, element, index) => [index, element]);
134
606
  }
607
+
608
+ /**
609
+ * Removes all duplicate elements from the reduced iterator.
610
+ * The first occurrence of each element will be kept.
611
+ *
612
+ * Since the iterator is lazy, the deduplication process will
613
+ * be executed once the resulting iterator is materialized.
614
+ *
615
+ * A new iterator will be created, holding the reference to the original one.
616
+ * This means that the original iterator won't be consumed until the
617
+ * new one is and that consuming one of them will consume the other as well.
618
+ *
619
+ * ```ts
620
+ * const results = new ReducedIterator<number>([-3, -1, 0, 2, 3, 6, -3, -1, 1, 5, 6, 8, 7, 2])
621
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
622
+ * .map((key, value) => Math.abs(value))
623
+ * .reduce((key, accumulator, value) => accumulator + value)
624
+ * .unique();
625
+ *
626
+ * console.log(results.toObject()); // { odd: 24 }
627
+ *
628
+ * @returns A new {@link ReducedIterator} containing only the unique elements.
629
+ */
135
630
  public unique(): ReducedIterator<K, T>
136
631
  {
137
632
  const elements = this._elements;
@@ -139,7 +634,6 @@ export default class ReducedIterator<K extends PropertyKey, T>
139
634
  return new ReducedIterator(function* ()
140
635
  {
141
636
  const values = new Set<T>();
142
-
143
637
  for (const [key, element] of elements)
144
638
  {
145
639
  if (values.has(element)) { continue; }
@@ -150,6 +644,23 @@ export default class ReducedIterator<K extends PropertyKey, T>
150
644
  });
151
645
  }
152
646
 
647
+ /**
648
+ * Counts the number of elements in the reduced iterator.
649
+ * This method will consume the entire iterator in the process.
650
+ *
651
+ * If the iterator is infinite, the method will never return.
652
+ *
653
+ * ```ts
654
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
655
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
656
+ * .reduce((key, accumulator, value) => accumulator + value)
657
+ * .count();
658
+ *
659
+ * console.log(results); // 2
660
+ * ```
661
+ *
662
+ * @returns The number of elements in the iterator.
663
+ */
153
664
  public count(): number
154
665
  {
155
666
  let index = 0;
@@ -159,6 +670,26 @@ export default class ReducedIterator<K extends PropertyKey, T>
159
670
  return index;
160
671
  }
161
672
 
673
+ /**
674
+ * Iterates over all elements of the reduced iterator.
675
+ * The elements are passed to the function along with their key and index.
676
+ *
677
+ * This method will consume the entire iterator in the process.
678
+ * If the iterator is infinite, the method will never return.
679
+ *
680
+ * ```ts
681
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
682
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
683
+ * .reduce((key, accumulator, value) => accumulator + value);
684
+ *
685
+ * reduced.forEach((key, value, index) =>
686
+ * {
687
+ * console.log(`#${index} - ${key}: ${value}`); // "#0 - odd: 4", "#1 - even: 16"
688
+ * });
689
+ * ```
690
+ *
691
+ * @param iteratee The function to apply to each element of the reduced iterator.
692
+ */
162
693
  public forEach(iteratee: KeyedIteratee<K, T>): void
163
694
  {
164
695
  for (const [index, [key, element]] of this._elements.enumerate())
@@ -167,6 +698,67 @@ export default class ReducedIterator<K extends PropertyKey, T>
167
698
  }
168
699
  }
169
700
 
701
+ /**
702
+ * Reaggregates the elements of the reduced iterator.
703
+ * The elements are grouped by a new key computed by the given iteratee function.
704
+ *
705
+ * Since the iterator is lazy, the reorganizing process will
706
+ * be executed once the resulting iterator is materialized.
707
+ *
708
+ * A new iterator will be created, holding the reference to the original one.
709
+ * This means that the original iterator won't be consumed until the
710
+ * new one is and that consuming one of them will consume the other as well.
711
+ *
712
+ * ```ts
713
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, -6, -8])
714
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
715
+ * .reduce((key, accumulator, value) => accumulator + value)
716
+ * .reorganizeBy((key, value) => value > 0 ? "positive" : "negative");
717
+ *
718
+ * console.log(results.toObject()); // { positive: 4, negative: -12 }
719
+ * ```
720
+ *
721
+ * @template J The type of the new keys used to group the elements.
722
+ *
723
+ * @param iteratee The function to determine the new key of each element of the iterator.
724
+ *
725
+ * @returns A new {@link AggregatedIterator} containing the elements reorganized by the new keys.
726
+ */
727
+ public reorganizeBy<J extends PropertyKey>(iteratee: KeyedIteratee<K, T, J>): AggregatedIterator<J, T>
728
+ {
729
+ const elements = this._elements.enumerate();
730
+
731
+ return new AggregatedIterator(function* ()
732
+ {
733
+ for (const [index, [key, element]] of elements)
734
+ {
735
+ yield [iteratee(key, element, index), element];
736
+ }
737
+ });
738
+ }
739
+
740
+ /**
741
+ * An utility method that returns a new {@link SmartIterator}
742
+ * object containing all the keys of the iterator.
743
+ *
744
+ * Since the iterator is lazy, the keys will be extracted
745
+ * be executed once the resulting iterator is materialized.
746
+ *
747
+ * A new iterator will be created, holding the reference to the original one.
748
+ * This means that the original iterator won't be consumed until the
749
+ * new one is and that consuming one of them will consume the other as well.
750
+ *
751
+ * ```ts
752
+ * const keys = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
753
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
754
+ * .reduce((key, accumulator, value) => accumulator + value)
755
+ * .keys();
756
+ *
757
+ * console.log(keys.toArray()); // ["odd", "even"]
758
+ * ```
759
+ *
760
+ * @returns A new {@link SmartIterator} containing all the keys of the iterator.
761
+ */
170
762
  public keys(): SmartIterator<K>
171
763
  {
172
764
  const elements = this._elements;
@@ -179,10 +771,57 @@ export default class ReducedIterator<K extends PropertyKey, T>
179
771
  }
180
772
  });
181
773
  }
182
- public items(): SmartIterator<[K, T]>
774
+
775
+ /**
776
+ * An utility method that returns a new {@link SmartIterator}
777
+ * object containing all the entries of the iterator.
778
+ * Each entry is a tuple containing the key and the element.
779
+ *
780
+ * Since the iterator is lazy, the entries will be extracted
781
+ * be executed once the resulting iterator is materialized.
782
+ *
783
+ * A new iterator will be created, holding the reference to the original one.
784
+ * This means that the original iterator won't be consumed until the
785
+ * new one is and that consuming one of them will consume the other as well.
786
+ *
787
+ * ```ts
788
+ * const entries = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
789
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
790
+ * .reduce((key, accumulator, value) => accumulator + value)
791
+ * .entries();
792
+ *
793
+ * console.log(entries.toArray()); // [["odd", 4], ["even", 16]]
794
+ * ```
795
+ *
796
+ * @returns A new {@link SmartIterator} containing all the entries of the iterator.
797
+ */
798
+ public entries(): SmartIterator<[K, T]>
183
799
  {
184
800
  return this._elements;
185
801
  }
802
+
803
+ /**
804
+ * An utility method that returns a new {@link SmartIterator}
805
+ * object containing all the values of the iterator.
806
+ *
807
+ * Since the iterator is lazy, the values will be extracted
808
+ * be executed once the resulting iterator is materialized.
809
+ *
810
+ * A new iterator will be created, holding the reference to the original one.
811
+ * This means that the original iterator won't be consumed until the
812
+ * new one is and that consuming one of them will consume the other as well.
813
+ *
814
+ * ```ts
815
+ * const values = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
816
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
817
+ * .reduce((key, accumulator, value) => accumulator + value)
818
+ * .values();
819
+ *
820
+ * console.log(values.toArray()); // [4, 16]
821
+ * ```
822
+ *
823
+ * @returns A new {@link SmartIterator} containing all the values of the iterator.
824
+ */
186
825
  public values(): SmartIterator<T>
187
826
  {
188
827
  const elements = this._elements;
@@ -196,17 +835,67 @@ export default class ReducedIterator<K extends PropertyKey, T>
196
835
  });
197
836
  }
198
837
 
838
+ /**
839
+ * Materializes the iterator into an array.
840
+ * This method will consume the entire iterator in the process.
841
+ *
842
+ * If the iterator is infinite, the method will never return.
843
+ *
844
+ * ```ts
845
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
846
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
847
+ * .reduce((key, accumulator, value) => accumulator + value);
848
+ *
849
+ * console.log(reduced.toArray()); // [4, 16]
850
+ * ```
851
+ *
852
+ * @returns The {@link Array} containing all elements of the iterator.
853
+ */
199
854
  public toArray(): T[]
200
855
  {
201
856
  return Array.from(this.values());
202
857
  }
858
+
859
+ /**
860
+ * Materializes the iterator into a map.
861
+ * This method will consume the entire iterator in the process.
862
+ *
863
+ * If the iterator is infinite, the method will never return.
864
+ *
865
+ * ```ts
866
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
867
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
868
+ * .reduce((key, accumulator, value) => accumulator + value);
869
+ *
870
+ * console.log(reduced.toMap()); // Map(2) { "odd" => 4, "even" => 16 }
871
+ * ```
872
+ *
873
+ * @returns The {@link Map} containing all elements of the iterator.
874
+ */
203
875
  public toMap(): Map<K, T>
204
876
  {
205
- return new Map(this.items());
877
+ return new Map(this.entries());
206
878
  }
879
+
880
+ /**
881
+ * Materializes the iterator into an object.
882
+ * This method will consume the entire iterator in the process.
883
+ *
884
+ * If the iterator is infinite, the method will never return.
885
+ *
886
+ * ```ts
887
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
888
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
889
+ * .reduce((key, accumulator, value) => accumulator + value);
890
+ *
891
+ * console.log(reduced.toObject()); // { odd: 4, even: 16 }
892
+ * ```
893
+ *
894
+ * @returns The {@link Object} containing all elements of the iterator.
895
+ */
207
896
  public toObject(): Record<K, T>
208
897
  {
209
- return Object.fromEntries(this.items()) as Record<K, T>;
898
+ return Object.fromEntries(this.entries()) as Record<K, T>;
210
899
  }
211
900
 
212
901
  public readonly [Symbol.toStringTag]: string = "ReducedIterator";