@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
@@ -3,21 +3,161 @@ 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";
6
+ import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardPredicate } from "./types.js";
7
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
+ * ---
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const results = new ReducedIterator<string, number>([["A", 1], ["B", 2], ["C", 4]]);
53
+ * ```
54
+ *
55
+ * ---
56
+ *
57
+ * @param iterable A reduced iterable object.
58
+ */
12
59
  public constructor(iterable: Iterable<[K, T]>);
60
+
61
+ /**
62
+ * Initializes a new instance of the {@link ReducedIterator} class.
63
+ *
64
+ * ---
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const results = new ReducedIterator<string, number>({
69
+ * _index: 0,
70
+ * next: () =>
71
+ * {
72
+ * if (this._index >= 3) { return { done: true, value: undefined }; }
73
+ * this._index += 1;
74
+ *
75
+ * return { done: false, value: [["A", "B", "C"][this._index], (this._index + 1)] };
76
+ * }
77
+ * });
78
+ * ```
79
+ *
80
+ * ---
81
+ *
82
+ * @param iterator An reduced iterator object.
83
+ */
13
84
  public constructor(iterator: Iterator<[K, T]>);
85
+
86
+ /**
87
+ * Initializes a new instance of the {@link ReducedIterator} class.
88
+ *
89
+ * ---
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { range, Random } from "@byloth/core";
94
+ *
95
+ * const results = new ReducedIterator<string, number>(function* ()
96
+ * {
97
+ * for (const index of range(3))
98
+ * {
99
+ * yield [["A", "B", "C"][index], (index + 1)];
100
+ * }
101
+ * });
102
+ * ```
103
+ *
104
+ * ---
105
+ *
106
+ * @param generatorFn A generator function that produces the reduced elements.
107
+ */
14
108
  public constructor(generatorFn: GeneratorFunction<[K, T]>);
109
+
110
+ /**
111
+ * Initializes a new instance of the {@link ReducedIterator} class.
112
+ *
113
+ * ---
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * const results = new ReducedIterator(reducedValues);
118
+ * ```
119
+ *
120
+ * ---
121
+ *
122
+ * @param argument An iterable, iterator or generator function that produces the reduced elements.
123
+ */
15
124
  public constructor(argument: Iterable<[K, T]> | Iterator<[K, T]> | GeneratorFunction<[K, T]>);
16
125
  public constructor(argument: Iterable<[K, T]> | Iterator<[K, T]> | GeneratorFunction<[K, T]>)
17
126
  {
18
127
  this._elements = new SmartIterator(argument);
19
128
  }
20
129
 
130
+ /**
131
+ * Determines whether all elements of the reduced iterator satisfy the given condition.
132
+ * See also {@link ReducedIterator.some}.
133
+ *
134
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
135
+ * Once a single element doesn't satisfy the condition, the method will return `false` immediately.
136
+ *
137
+ * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
138
+ * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
139
+ * Consider using {@link ReducedIterator.find} instead.
140
+ *
141
+ * If the iterator is infinite and every element satisfies the condition, the method will never return.
142
+ *
143
+ * ---
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
148
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
149
+ * .reduce((key, accumulator, value) => accumulator + value)
150
+ * .every((key, value) => value > 0);
151
+ *
152
+ * console.log(results); // true
153
+ * ```
154
+ *
155
+ * ---
156
+ *
157
+ * @param predicate The condition to check for each element of the iterator.
158
+ *
159
+ * @returns `true` if all elements satisfy the condition, `false` otherwise.
160
+ */
21
161
  public every(predicate: KeyedIteratee<K, T, boolean>): boolean
22
162
  {
23
163
  for (const [index, [key, element]] of this._elements.enumerate())
@@ -27,6 +167,38 @@ export default class ReducedIterator<K extends PropertyKey, T>
27
167
 
28
168
  return true;
29
169
  }
170
+
171
+ /**
172
+ * Determines whether any element of the reduced iterator satisfies the given condition.
173
+ * See also {@link ReducedIterator.every}.
174
+ *
175
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
176
+ * Once a single element satisfies the condition, the method will return `true` immediately.
177
+ *
178
+ * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
179
+ * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
180
+ * Consider using {@link ReducedIterator.find} instead.
181
+ *
182
+ * If the iterator is infinite and no element satisfies the condition, the method will never return.
183
+ *
184
+ * ---
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
189
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
190
+ * .reduce((key, accumulator, value) => accumulator + value)
191
+ * .some((key, value) => value > 0);
192
+ *
193
+ * console.log(results); // true
194
+ * ```
195
+ *
196
+ * ---
197
+ *
198
+ * @param predicate The condition to check for each element of the iterator.
199
+ *
200
+ * @returns `true` if any element satisfies the condition, `false` otherwise.
201
+ */
30
202
  public some(predicate: KeyedIteratee<K, T, boolean>): boolean
31
203
  {
32
204
  for (const [index, [key, element]] of this._elements.enumerate())
@@ -37,8 +209,77 @@ export default class ReducedIterator<K extends PropertyKey, T>
37
209
  return false;
38
210
  }
39
211
 
212
+ /**
213
+ * Filters the elements of the reduced iterator using a given condition.
214
+ *
215
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
216
+ * If the condition is met, the element will be included in the new iterator.
217
+ *
218
+ * Since the iterator is lazy, the filtering process will
219
+ * be executed once the resulting iterator is materialized.
220
+ *
221
+ * A new iterator will be created, holding the reference to the original one.
222
+ * This means that the original iterator won't be consumed until the
223
+ * new one is and that consuming one of them will consume the other as well.
224
+ *
225
+ * ---
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
230
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
231
+ * .reduce((key, accumulator, value) => accumulator + value)
232
+ * .filter((key, value) => value > 0);
233
+ *
234
+ * console.log(results.toObject()); // { odd: 4, even: 16 }
235
+ * ```
236
+ *
237
+ * ---
238
+ *
239
+ * @param predicate The condition to check for each element of the iterator.
240
+ *
241
+ * @returns A new {@link ReducedIterator} containing only the elements that satisfy the condition.
242
+ */
40
243
  public filter(predicate: KeyedIteratee<K, T, boolean>): ReducedIterator<K, T>;
41
- public filter<S extends T>(predicate: KeyedTypeGuardIteratee<K, T, S>): ReducedIterator<K, S>;
244
+
245
+ /**
246
+ * Filters the elements of the reduced iterator using a given type guard predicate.
247
+ *
248
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
249
+ * If the condition is met, the element will be included in the new iterator.
250
+ *
251
+ * Since the iterator is lazy, the filtering process will
252
+ * be executed once the resulting iterator is materialized.
253
+ *
254
+ * A new iterator will be created, holding the reference to the original one.
255
+ * This means that the original iterator won't be consumed until the
256
+ * new one is and that consuming one of them will consume the other as well.
257
+ *
258
+ * ---
259
+ *
260
+ * @example
261
+ * ```ts
262
+ * const results = new SmartIterator<number | string>([-3, -1, "0", "2", 3, 5, "6", "8"])
263
+ * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd")
264
+ * .reduce((key, accumulator, value) => accumulator + value)
265
+ * .filter<number>((key, value) => typeof value === "number");
266
+ *
267
+ * console.log(results.toObject()); // { odd: 4 }
268
+ * ```
269
+ *
270
+ * ---
271
+ *
272
+ * @template S
273
+ * The type of the elements that satisfy the condition.
274
+ * This allows the type-system to infer the correct type of the iterator.
275
+ *
276
+ * It must be a subtype of the original type of the elements.
277
+ *
278
+ * @param predicate The type guard condition to check for each element of the iterator.
279
+ *
280
+ * @returns A new {@link ReducedIterator} containing only the elements that satisfy the condition.
281
+ */
282
+ public filter<S extends T>(predicate: KeyedTypeGuardPredicate<K, T, S>): ReducedIterator<K, S>;
42
283
  public filter(predicate: KeyedIteratee<K, T, boolean>): ReducedIterator<K, T>
43
284
  {
44
285
  const elements = this._elements.enumerate();
@@ -51,6 +292,40 @@ export default class ReducedIterator<K extends PropertyKey, T>
51
292
  }
52
293
  });
53
294
  }
295
+
296
+ /**
297
+ * Maps the elements of the reduced iterator using a given transformation function.
298
+ *
299
+ * This method will iterate over all the elements of the iterator applying the transformation function.
300
+ * The result of the transformation will be included in the new iterator.
301
+ *
302
+ * Since the iterator is lazy, the mapping process will
303
+ * be executed once the resulting iterator is materialized.
304
+ *
305
+ * A new iterator will be created, holding the reference to the original one.
306
+ * This means that the original iterator won't be consumed until the
307
+ * new one is and that consuming one of them will consume the other as well.
308
+ *
309
+ * ---
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
314
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
315
+ * .reduce((key, accumulator, value) => accumulator + value)
316
+ * .map((key, value) => value * 2);
317
+ *
318
+ * console.log(results.toObject()); // { odd: 8, even: 32 }
319
+ * ```
320
+ *
321
+ * ---
322
+ *
323
+ * @template V The type of the elements after the transformation.
324
+ *
325
+ * @param iteratee The transformation function to apply to each element of the iterator.
326
+ *
327
+ * @returns A new {@link ReducedIterator} containing the transformed elements.
328
+ */
54
329
  public map<V>(iteratee: KeyedIteratee<K, T, V>): ReducedIterator<K, V>
55
330
  {
56
331
  const elements = this._elements.enumerate();
@@ -63,7 +338,74 @@ export default class ReducedIterator<K extends PropertyKey, T>
63
338
  }
64
339
  });
65
340
  }
341
+
342
+ /**
343
+ * Reduces the elements of the reduced iterator using a given reducer function.
344
+ * This method will consume the entire iterator in the process.
345
+ *
346
+ * It will iterate over all the elements of the iterator applying the reducer function.
347
+ * The result of each iteration will be passed as the accumulator to the next one.
348
+ *
349
+ * The first accumulator value will be the first element of the iterator.
350
+ * The last accumulator value will be the final result of the reduction.
351
+ *
352
+ * Also note that:
353
+ * - If an empty iterator is provided, a {@link ValueException} will be thrown.
354
+ * - If the iterator is infinite, the method will never return.
355
+ *
356
+ * ---
357
+ *
358
+ * @example
359
+ * ```ts
360
+ * const result = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
361
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
362
+ * .reduce((key, accumulator, value) => accumulator + value)
363
+ * .reduce((key, accumulator, value) => accumulator + value);
364
+ *
365
+ * console.log(result); // 20
366
+ * ```
367
+ *
368
+ * ---
369
+ *
370
+ * @param reducer The reducer function to apply to the elements of the iterator.
371
+ *
372
+ * @returns The final value after reducing all the elements of the iterator.
373
+ */
66
374
  public reduce(reducer: KeyedReducer<K, T, T>): T;
375
+
376
+ /**
377
+ * Reduces the elements of the reduced iterator using a given reducer function.
378
+ * This method will consume the entire iterator in the process.
379
+ *
380
+ * It will iterate over all the elements of the iterator applying the reducer function.
381
+ * The result of each iteration will be passed as the accumulator to the next one.
382
+ *
383
+ * The first accumulator value will be the provided initial value.
384
+ * The last accumulator value will be the final result of the reduction.
385
+ *
386
+ * If the iterator is infinite, the method will never return.
387
+ *
388
+ * ---
389
+ *
390
+ * @example
391
+ * ```ts
392
+ * const result = 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 + value)
395
+ * .reduce((key, { value }, currentValue) => ({ value: value + currentValue }), { value: 0 });
396
+ *
397
+ * console.log(result); // { value: 20 }
398
+ * ```
399
+ *
400
+ * ---
401
+ *
402
+ * @template A The type of the accumulator value which will also be the type of the final result of the reduction.
403
+ *
404
+ * @param reducer The reducer function to apply to the elements of the iterator.
405
+ * @param initialValue The initial value of the accumulator.
406
+ *
407
+ * @returns The final result of the reduction.
408
+ */
67
409
  public reduce<A>(reducer: KeyedReducer<K, T, A>, initialValue: A): A;
68
410
  public reduce<A>(reducer: KeyedReducer<K, T, A>, initialValue?: A): A
69
411
  {
@@ -88,7 +430,40 @@ export default class ReducedIterator<K extends PropertyKey, T>
88
430
  return accumulator;
89
431
  }
90
432
 
91
- public flatMap<V>(iteratee: KeyedIteratee<K, T, Iterable<V>>): AggregatedIterator<K, V>
433
+ /**
434
+ * Flattens the elements of the reduced iterator using a given transformation function.
435
+ *
436
+ * This method will iterate over all the elements of the iterator applying the transformation function.
437
+ * The result of each transformation will be flattened into the new iterator.
438
+ *
439
+ * Since the iterator is lazy, the flattening process will
440
+ * be executed once the resulting iterator is materialized.
441
+ *
442
+ * A new iterator will be created, holding the reference to the original one.
443
+ * This means that the original iterator won't be consumed until the
444
+ * new one is and that consuming one of them will consume the other as well.
445
+ *
446
+ * ---
447
+ *
448
+ * @example
449
+ * ```ts
450
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
451
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
452
+ * .reduce((key, accumulator, value) => accumulator.concat([value]), () => [])
453
+ * .flatMap((key, value) => value);
454
+ *
455
+ * console.log(results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] }
456
+ * ```
457
+ *
458
+ * ---
459
+ *
460
+ * @template V The type of the elements after the transformation.
461
+ *
462
+ * @param iteratee The transformation function to apply to each element of the iterator.
463
+ *
464
+ * @returns A new {@link AggregatedIterator} containing the flattened elements.
465
+ */
466
+ public flatMap<V>(iteratee: KeyedIteratee<K, T, V | readonly V[]>): AggregatedIterator<K, V>
92
467
  {
93
468
  const elements = this._elements.enumerate();
94
469
 
@@ -96,11 +471,48 @@ export default class ReducedIterator<K extends PropertyKey, T>
96
471
  {
97
472
  for (const [index, [key, element]] of elements)
98
473
  {
99
- for (const value of iteratee(key, element, index)) { yield [key, value]; }
474
+ const values = iteratee(key, element, index);
475
+
476
+ if (values instanceof Array)
477
+ {
478
+ for (const value of values) { yield [key, value]; }
479
+ }
480
+ else { yield [key, values]; }
100
481
  }
101
482
  });
102
483
  }
103
484
 
485
+ /**
486
+ * Drops a given number of elements at the beginning of the reduced iterator.
487
+ * The remaining elements will be included in the new iterator.
488
+ * See also {@link ReducedIterator.take}.
489
+ *
490
+ * Since the iterator is lazy, the dropping process will
491
+ * be executed once the resulting iterator is materialized.
492
+ *
493
+ * A new iterator will be created, holding the reference to the original one.
494
+ * This means that the original iterator won't be consumed until the
495
+ * new one is and that consuming one of them will consume the other as well.
496
+ *
497
+ * Only the dropped elements will be consumed in the process.
498
+ * The rest of the iterator will be consumed once the new iterator is.
499
+ *
500
+ * ---
501
+ *
502
+ * @example
503
+ * ```ts
504
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
505
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
506
+ * .reduce((key, accumulator, value) => accumulator.concat(value), () => [])
507
+ * .drop(1);
508
+ *
509
+ * console.log(results.toObject()); // { even: [0, 2, 6, 8] }
510
+ * ```
511
+ *
512
+ * @param count The number of elements to drop.
513
+ *
514
+ * @returns A new {@link ReducedIterator} containing the remaining elements.
515
+ */
104
516
  public drop(count: number): ReducedIterator<K, T>
105
517
  {
106
518
  const elements = this._elements.enumerate();
@@ -113,7 +525,43 @@ export default class ReducedIterator<K extends PropertyKey, T>
113
525
  }
114
526
  });
115
527
  }
116
- public take(count: number): ReducedIterator<K, T>
528
+
529
+ /**
530
+ * Takes a given number of elements at the beginning of the reduced iterator.
531
+ * The elements will be included in the new iterator.
532
+ * See also {@link ReducedIterator.drop}.
533
+ *
534
+ * Since the iterator is lazy, the taking process will
535
+ * be executed once the resulting iterator is materialized.
536
+ *
537
+ * A new iterator will be created, holding the reference to the original one.
538
+ * This means that the original iterator won't be consumed until the
539
+ * new one is and that consuming one of them will consume the other as well.
540
+ *
541
+ * Only the taken elements will be consumed from the original reduced iterator.
542
+ * The rest of the original reduced iterator will be available for further consumption.
543
+ *
544
+ * ---
545
+ *
546
+ * @example
547
+ * ```ts
548
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
549
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
550
+ * .reduce((key, accumulator, value) => accumulator.concat(value), () => []);
551
+ *
552
+ * const results = iterator.take(1);
553
+ *
554
+ * console.log(results.toObject()); // { odd: [-3, -1, 3, 5] }
555
+ * console.log(reduced.toObject()); // { even: [0, 2, 6, 8] }
556
+ * ```
557
+ *
558
+ * ---
559
+ *
560
+ * @param limit The number of elements to take.
561
+ *
562
+ * @returns A new {@link ReducedIterator} containing the taken elements.
563
+ */
564
+ public take(limit: number): ReducedIterator<K, T>
117
565
  {
118
566
  const elements = this._elements.enumerate();
119
567
 
@@ -121,17 +569,146 @@ export default class ReducedIterator<K extends PropertyKey, T>
121
569
  {
122
570
  for (const [index, [key, element]] of elements)
123
571
  {
124
- if (index >= count) { break; }
125
-
572
+ if (index >= limit) { break; }
126
573
  yield [key, element];
127
574
  }
128
575
  });
129
576
  }
130
577
 
578
+ /**
579
+ * Finds the first element of the reduced iterator that satisfies the given condition.
580
+ *
581
+ * This method will iterate over all the elements of the iterator checking if they satisfy the condition.
582
+ * The first element that satisfies the condition will be returned immediately.
583
+ *
584
+ * Only the elements that are necessary to find the first
585
+ * satisfying one will be consumed from the original iterator.
586
+ * The rest of the iterator will be available for further consumption.
587
+ *
588
+ * Also note that:
589
+ * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed.
590
+ * - If the iterator is infinite and no element satisfies the condition, the method will never return.
591
+ *
592
+ * ---
593
+ *
594
+ * @example
595
+ * ```ts
596
+ * const results = new SmartIterator<number>([-3, -3, -1, 0, 1, 2, 5, 6, 8])
597
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
598
+ * .reduce((key, accumulator, value) => accumulator + value)
599
+ * .find((key, value) => value > 0);
600
+ *
601
+ * console.log(results); // 16
602
+ *
603
+ * @param predicate The condition to check for each element of the iterator.
604
+ *
605
+ * @returns The first element that satisfies the condition, `undefined` otherwise.
606
+ */
607
+ public find(predicate: KeyedIteratee<K, T, boolean>): T | undefined;
608
+
609
+ /**
610
+ * Finds the first element of the reduced iterator that satisfies the given type guard predicate.
611
+ *
612
+ * This method will iterate over all the 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 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 results = new SmartIterator<number | string>(["-3", -3, "-1", 0, 1, 2, "5", 6, 8])
628
+ * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd")
629
+ * .reduce((key, accumulator, value) => accumulator + value)
630
+ * .find<number>((key, value) => typeof value === "number");
631
+ *
632
+ * console.log(results); // 16
633
+ *
634
+ * @template S
635
+ * The type of the elements that satisfy the condition.
636
+ * This allows the type-system to infer the correct type of the result.
637
+ *
638
+ * It must be a subtype of the original type of the elements.
639
+ *
640
+ * @param predicate The type guard condition to check for each element of the iterator.
641
+ *
642
+ * @returns The first element that satisfies the condition, `undefined` otherwise.
643
+ */
644
+ public find<S extends T>(predicate: KeyedTypeGuardPredicate<K, T, S>): S | undefined;
645
+ public find(predicate: KeyedIteratee<K, T, boolean>): T | undefined
646
+ {
647
+ for (const [index, [key, element]] of this._elements.enumerate())
648
+ {
649
+ if (predicate(key, element, index)) { return element; }
650
+ }
651
+
652
+ return undefined;
653
+ }
654
+
655
+ /**
656
+ * Enumerates the elements of the reduced iterator.
657
+ * Each element is paired with its index in a new iterator.
658
+ *
659
+ * Since the iterator is lazy, the enumeration process will
660
+ * be executed once the resulting iterator is materialized.
661
+ *
662
+ * A new iterator will be created, holding the reference to the original one.
663
+ * This means that the original iterator won't be consumed until the
664
+ * new one is and that consuming one of them will consume the other as well.
665
+ *
666
+ * ---
667
+ *
668
+ * @example
669
+ * ```ts
670
+ * const results = new ReducedIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
671
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
672
+ * .reduce((key, accumulator, value) => accumulator + value)
673
+ * .enumerate();
674
+ *
675
+ * console.log(results.toObject()); // [[0, 4], [1, 16]]
676
+ * ```
677
+ *
678
+ * ---
679
+ *
680
+ * @returns A new {@link ReducedIterator} object containing the enumerated elements.
681
+ */
131
682
  public enumerate(): ReducedIterator<K, [number, T]>
132
683
  {
133
684
  return this.map((_, element, index) => [index, element]);
134
685
  }
686
+
687
+ /**
688
+ * Removes all duplicate elements from the reduced iterator.
689
+ * The first occurrence of each element will be kept.
690
+ *
691
+ * Since the iterator is lazy, the deduplication process will
692
+ * be executed once the resulting iterator is materialized.
693
+ *
694
+ * A new iterator will be created, holding the reference to the original one.
695
+ * This means that the original iterator won't be consumed until the
696
+ * new one is and that consuming one of them will consume the other as well.
697
+ *
698
+ * ---
699
+ *
700
+ * @example
701
+ * ```ts
702
+ * const results = new ReducedIterator<number>([-3, -1, 0, 2, 3, 6, -3, -1, 1, 5, 6, 8, 7, 2])
703
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
704
+ * .map((key, value) => Math.abs(value))
705
+ * .reduce((key, accumulator, value) => accumulator + value)
706
+ * .unique();
707
+ *
708
+ * console.log(results.toObject()); // { odd: 24 }
709
+ *
710
+ * @returns A new {@link ReducedIterator} containing only the unique elements.
711
+ */
135
712
  public unique(): ReducedIterator<K, T>
136
713
  {
137
714
  const elements = this._elements;
@@ -139,7 +716,6 @@ export default class ReducedIterator<K extends PropertyKey, T>
139
716
  return new ReducedIterator(function* ()
140
717
  {
141
718
  const values = new Set<T>();
142
-
143
719
  for (const [key, element] of elements)
144
720
  {
145
721
  if (values.has(element)) { continue; }
@@ -150,6 +726,28 @@ export default class ReducedIterator<K extends PropertyKey, T>
150
726
  });
151
727
  }
152
728
 
729
+ /**
730
+ * Counts the number of elements in the reduced iterator.
731
+ * This method will consume the entire iterator in the process.
732
+ *
733
+ * If the iterator is infinite, the method will never return.
734
+ *
735
+ * ---
736
+ *
737
+ * @example
738
+ * ```ts
739
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
740
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
741
+ * .reduce((key, accumulator, value) => accumulator + value)
742
+ * .count();
743
+ *
744
+ * console.log(results); // 2
745
+ * ```
746
+ *
747
+ * ---
748
+ *
749
+ * @returns The number of elements in the iterator.
750
+ */
153
751
  public count(): number
154
752
  {
155
753
  let index = 0;
@@ -159,6 +757,31 @@ export default class ReducedIterator<K extends PropertyKey, T>
159
757
  return index;
160
758
  }
161
759
 
760
+ /**
761
+ * Iterates over all elements of the reduced iterator.
762
+ * The elements are passed to the function along with their key and index.
763
+ *
764
+ * This method will consume the entire iterator in the process.
765
+ * If the iterator is infinite, the method will never return.
766
+ *
767
+ * ---
768
+ *
769
+ * @example
770
+ * ```ts
771
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
772
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
773
+ * .reduce((key, accumulator, value) => accumulator + value);
774
+ *
775
+ * reduced.forEach((key, value, index) =>
776
+ * {
777
+ * console.log(`#${index} - ${key}: ${value}`); // "#0 - odd: 4", "#1 - even: 16"
778
+ * });
779
+ * ```
780
+ *
781
+ * ---
782
+ *
783
+ * @param iteratee The function to apply to each element of the reduced iterator.
784
+ */
162
785
  public forEach(iteratee: KeyedIteratee<K, T>): void
163
786
  {
164
787
  for (const [index, [key, element]] of this._elements.enumerate())
@@ -167,6 +790,77 @@ export default class ReducedIterator<K extends PropertyKey, T>
167
790
  }
168
791
  }
169
792
 
793
+ /**
794
+ * Reaggregates the elements of the reduced iterator.
795
+ * The elements are grouped by a new key computed by the given iteratee function.
796
+ *
797
+ * Since the iterator is lazy, the reorganizing process will
798
+ * be executed once the resulting iterator is materialized.
799
+ *
800
+ * A new iterator will be created, holding the reference to the original one.
801
+ * This means that the original iterator won't be consumed until the
802
+ * new one is and that consuming one of them will consume the other as well.
803
+ *
804
+ * ---
805
+ *
806
+ * @example
807
+ * ```ts
808
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, -6, -8])
809
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
810
+ * .reduce((key, accumulator, value) => accumulator + value)
811
+ * .reorganizeBy((key, value) => value > 0 ? "positive" : "negative");
812
+ *
813
+ * console.log(results.toObject()); // { positive: 4, negative: -12 }
814
+ * ```
815
+ *
816
+ * ---
817
+ *
818
+ * @template J The type of the new keys used to group the elements.
819
+ *
820
+ * @param iteratee The function to determine the new key of each element of the iterator.
821
+ *
822
+ * @returns A new {@link AggregatedIterator} containing the elements reorganized by the new keys.
823
+ */
824
+ public reorganizeBy<J extends PropertyKey>(iteratee: KeyedIteratee<K, T, J>): AggregatedIterator<J, T>
825
+ {
826
+ const elements = this._elements.enumerate();
827
+
828
+ return new AggregatedIterator(function* ()
829
+ {
830
+ for (const [index, [key, element]] of elements)
831
+ {
832
+ yield [iteratee(key, element, index), element];
833
+ }
834
+ });
835
+ }
836
+
837
+ /**
838
+ * An utility method that returns a new {@link SmartIterator}
839
+ * object containing all the keys of the iterator.
840
+ *
841
+ * Since the iterator is lazy, the keys will be extracted
842
+ * be executed once the resulting iterator is materialized.
843
+ *
844
+ * A new iterator will be created, holding the reference to the original one.
845
+ * This means that the original iterator won't be consumed until the
846
+ * new one is and that consuming one of them will consume the other as well.
847
+ *
848
+ * ---
849
+ *
850
+ * @example
851
+ * ```ts
852
+ * const keys = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
853
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
854
+ * .reduce((key, accumulator, value) => accumulator + value)
855
+ * .keys();
856
+ *
857
+ * console.log(keys.toArray()); // ["odd", "even"]
858
+ * ```
859
+ *
860
+ * ---
861
+ *
862
+ * @returns A new {@link SmartIterator} containing all the keys of the iterator.
863
+ */
170
864
  public keys(): SmartIterator<K>
171
865
  {
172
866
  const elements = this._elements;
@@ -179,10 +873,67 @@ export default class ReducedIterator<K extends PropertyKey, T>
179
873
  }
180
874
  });
181
875
  }
182
- public items(): SmartIterator<[K, T]>
876
+
877
+ /**
878
+ * An utility method that returns a new {@link SmartIterator}
879
+ * object containing all the entries of the iterator.
880
+ * Each entry is a tuple containing the key and the element.
881
+ *
882
+ * Since the iterator is lazy, the entries will be extracted
883
+ * be executed once the resulting iterator is materialized.
884
+ *
885
+ * A new iterator will be created, holding the reference to the original one.
886
+ * This means that the original iterator won't be consumed until the
887
+ * new one is and that consuming one of them will consume the other as well.
888
+ *
889
+ * ---
890
+ *
891
+ * @example
892
+ * ```ts
893
+ * const entries = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
894
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
895
+ * .reduce((key, accumulator, value) => accumulator + value)
896
+ * .entries();
897
+ *
898
+ * console.log(entries.toArray()); // [["odd", 4], ["even", 16]]
899
+ * ```
900
+ *
901
+ * ---
902
+ *
903
+ * @returns A new {@link SmartIterator} containing all the entries of the iterator.
904
+ */
905
+ public entries(): SmartIterator<[K, T]>
183
906
  {
184
907
  return this._elements;
185
908
  }
909
+
910
+ /**
911
+ * An utility method that returns a new {@link SmartIterator}
912
+ * object containing all the values of the iterator.
913
+ *
914
+ * Since the iterator is lazy, the values will be extracted
915
+ * be executed once the resulting iterator is materialized.
916
+ *
917
+ * A new iterator will be created, holding the reference to the original one.
918
+ * This means that the original iterator won't be consumed until the
919
+ * new one is and that consuming one of them will consume the other as well.
920
+ *
921
+ * ---
922
+ *
923
+ * @example
924
+ * ```ts
925
+ * const values = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
926
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
927
+ * .reduce((key, accumulator, value) => accumulator + value)
928
+ * .values();
929
+ *
930
+ * console.log(values.toArray()); // [4, 16]
931
+ * ```
932
+ *
933
+ * ---
934
+ *
935
+ * @returns A new {@link SmartIterator} containing all the values of the iterator.
936
+ */
186
937
  public values(): SmartIterator<T>
187
938
  {
188
939
  const elements = this._elements;
@@ -196,17 +947,82 @@ export default class ReducedIterator<K extends PropertyKey, T>
196
947
  });
197
948
  }
198
949
 
950
+ /**
951
+ * Materializes the iterator into an array.
952
+ * This method will consume the entire iterator in the process.
953
+ *
954
+ * If the iterator is infinite, the method will never return.
955
+ *
956
+ * ---
957
+ *
958
+ * @example
959
+ * ```ts
960
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
961
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
962
+ * .reduce((key, accumulator, value) => accumulator + value);
963
+ *
964
+ * console.log(reduced.toArray()); // [4, 16]
965
+ * ```
966
+ *
967
+ * ---
968
+ *
969
+ * @returns The {@link Array} containing all elements of the iterator.
970
+ */
199
971
  public toArray(): T[]
200
972
  {
201
973
  return Array.from(this.values());
202
974
  }
975
+
976
+ /**
977
+ * Materializes the iterator into a map.
978
+ * This method will consume the entire iterator in the process.
979
+ *
980
+ * If the iterator is infinite, the method will never return.
981
+ *
982
+ * ---
983
+ *
984
+ * @example
985
+ * ```ts
986
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
987
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
988
+ * .reduce((key, accumulator, value) => accumulator + value);
989
+ *
990
+ * console.log(reduced.toMap()); // Map(2) { "odd" => 4, "even" => 16 }
991
+ * ```
992
+ *
993
+ * ---
994
+ *
995
+ * @returns The {@link Map} containing all elements of the iterator.
996
+ */
203
997
  public toMap(): Map<K, T>
204
998
  {
205
- return new Map(this.items());
999
+ return new Map(this.entries());
206
1000
  }
1001
+
1002
+ /**
1003
+ * Materializes the iterator into an object.
1004
+ * This method will consume the entire iterator in the process.
1005
+ *
1006
+ * If the iterator is infinite, the method will never return.
1007
+ *
1008
+ * ---
1009
+ *
1010
+ * @example
1011
+ * ```ts
1012
+ * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
1013
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
1014
+ * .reduce((key, accumulator, value) => accumulator + value);
1015
+ *
1016
+ * console.log(reduced.toObject()); // { odd: 4, even: 16 }
1017
+ * ```
1018
+ *
1019
+ * ---
1020
+ *
1021
+ * @returns The {@link Object} containing all elements of the iterator.
1022
+ */
207
1023
  public toObject(): Record<K, T>
208
1024
  {
209
- return Object.fromEntries(this.items()) as Record<K, T>;
1025
+ return Object.fromEntries(this.entries()) as Record<K, T>;
210
1026
  }
211
1027
 
212
1028
  public readonly [Symbol.toStringTag]: string = "ReducedIterator";