@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
@@ -9,24 +9,232 @@ import type {
9
9
  import type { MaybePromise } from "../types.js";
10
10
 
11
11
  import ReducedIterator from "./reduced-iterator.js";
12
- import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedTypeGuardIteratee, MaybeAsyncKeyedReducer } from "./types.js";
13
-
12
+ import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedReducer } from "./types.js";
13
+
14
+ /**
15
+ * A class representing an iterator that aggregates elements in a lazy and optimized way.
16
+ *
17
+ * It's part of the {@link SmartAsyncIterator} implementation,
18
+ * providing a way to group elements of an iterable by key.
19
+ * For this reason, it isn't recommended to instantiate this class directly
20
+ * (although it's still possible), but rather use the {@link SmartAsyncIterator.groupBy} method.
21
+ *
22
+ * It isn't directly iterable like its parent class but rather needs to specify on what you want to iterate.
23
+ * See the {@link AggregatedAsyncIterator.keys}, {@link AggregatedAsyncIterator.entries}
24
+ * & {@link AggregatedAsyncIterator.values} methods.
25
+ * It does, however, provide the same set of methods to perform
26
+ * operations and transformations on the elements of the iterator,
27
+ * having also the knowledge and context of the groups to which
28
+ * they belong, allowing to handle them in a grouped manner.
29
+ *
30
+ * This is particularly useful when you need to group elements and
31
+ * then perform specific operations on the groups themselves.
32
+ *
33
+ * ```ts
34
+ * const elements = fetch([...]); // Promise<[-3, -1, 0, 2, 3, 5, 6, 8]>;
35
+ * const results = new SmartAsyncIterator(elements)
36
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
37
+ * .count();
38
+ *
39
+ * console.log(await results.toObject()); // { odd: 4, even: 4 }
40
+ * ```
41
+ *
42
+ * @template K The type of the keys used to group the elements.
43
+ * @template T The type of the elements to aggregate.
44
+ */
14
45
  export default class AggregatedAsyncIterator<K extends PropertyKey, T>
15
46
  {
47
+ /**
48
+ * The internal {@link SmartAsyncIterator} object that holds the elements to aggregate.
49
+ */
16
50
  protected _elements: SmartAsyncIterator<[K, T]>;
17
51
 
52
+ /**
53
+ * Initializes a new instance of the {@link AggregatedAsyncIterator} class.
54
+ *
55
+ * ---
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const iterator = new AggregatedAsyncIterator<string, number>([["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]);
60
+ * ```
61
+ *
62
+ * ---
63
+ *
64
+ * @param iterable The iterable to aggregate.
65
+ */
18
66
  public constructor(iterable: Iterable<[K, T]>);
67
+
68
+ /**
69
+ * Initializes a new instance of the {@link AggregatedAsyncIterator} class.
70
+ *
71
+ * ---
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const elements = fetch([...]); // Promise<[["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]>
76
+ * const iterator = new AggregatedAsyncIterator<string, number>(elements);
77
+ * ```
78
+ *
79
+ * ---
80
+ *
81
+ * @param iterable The iterable to aggregate.
82
+ */
19
83
  public constructor(iterable: AsyncIterable<[K, T]>);
84
+
85
+ /**
86
+ * Initializes a new instance of the {@link AggregatedAsyncIterator} class.
87
+ *
88
+ * ---
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * import { Random } from "@byloth/core";
93
+ *
94
+ * const iterator = new AggregatedAsyncIterator<string, number>({
95
+ * _index: 0,
96
+ * next: () =>
97
+ * {
98
+ * if (this._index >= 5) { return { done: true, value: undefined }; }
99
+ * this._index += 1;
100
+ *
101
+ * return { done: false, value: [Random.Choice(["A", "B", "C"]), (this._index + 1)] };
102
+ * }
103
+ * });
104
+ * ```
105
+ *
106
+ * ---
107
+ *
108
+ * @param iterator The iterator to aggregate.
109
+ */
20
110
  public constructor(iterator: Iterator<[K, T]>);
111
+
112
+ /**
113
+ * Initializes a new instance of the {@link AggregatedAsyncIterator} class.
114
+ *
115
+ * ---
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * import { Random } from "@byloth/core";
120
+ *
121
+ * const iterator = new AggregatedAsyncIterator<string, number>({
122
+ * _index: 0,
123
+ * next: async () =>
124
+ * {
125
+ * if (this._index >= 5) { return { done: true, value: undefined }; }
126
+ * this._index += 1;
127
+ *
128
+ * return { done: false, value: [Random.Choice(["A", "B", "C"]), (this._index + 1)] };
129
+ * }
130
+ * });
131
+ * ```
132
+ *
133
+ * ---
134
+ *
135
+ * @param iterator The iterator to aggregate.
136
+ */
21
137
  public constructor(iterator: AsyncIterator<[K, T]>);
138
+
139
+ /**
140
+ * Initializes a new instance of the {@link AggregatedAsyncIterator} class.
141
+ *
142
+ * ---
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * import { range, Random } from "@byloth/core";
147
+ *
148
+ * const iterator = new AggregatedAsyncIterator<string, number>(function* ()
149
+ * {
150
+ * for (const index of range(5))
151
+ * {
152
+ * yield [Random.Choice(["A", "B", "C"]), (index + 1)];
153
+ * }
154
+ * });
155
+ * ```
156
+ *
157
+ * ---
158
+ *
159
+ * @param generatorFn The generator function to aggregate.
160
+ */
22
161
  public constructor(generatorFn: GeneratorFunction<[K, T]>);
162
+
163
+ /**
164
+ * Initializes a new instance of the {@link AggregatedAsyncIterator} class.
165
+ *
166
+ * ---
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * import { range, Random } from "@byloth/core";
171
+ *
172
+ * const iterator = new AggregatedAsyncIterator<string, number>(async function* ()
173
+ * {
174
+ * for await (const index of range(5))
175
+ * {
176
+ * yield [Random.Choice(["A", "B", "C"]), (index + 1)];
177
+ * }
178
+ * });
179
+ * ```
180
+ *
181
+ * ---
182
+ *
183
+ * @param generatorFn The generator function to aggregate.
184
+ */
23
185
  public constructor(generatorFn: AsyncGeneratorFunction<[K, T]>);
186
+
187
+ /**
188
+ * Initializes a new instance of the {@link AggregatedAsyncIterator} class.
189
+ *
190
+ * ---
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const iterator = new AggregatedAsyncIterator(asyncKeyedValues);
195
+ * ```
196
+ *
197
+ * ---
198
+ *
199
+ * @param argument The iterable, iterator or generator function to aggregate.
200
+ */
24
201
  public constructor(argument: MaybeAsyncIteratorLike<[K, T]> | MaybeAsyncGeneratorFunction<[K, T]>);
25
202
  public constructor(argument: MaybeAsyncIteratorLike<[K, T]> | MaybeAsyncGeneratorFunction<[K, T]>)
26
203
  {
27
204
  this._elements = new SmartAsyncIterator(argument);
28
205
  }
29
206
 
207
+ /**
208
+ * Determines whether all elements of each group of the iterator satisfy a given condition.
209
+ * See also {@link AggregatedAsyncIterator.some}.
210
+ * This method will consume the entire iterator in the process.
211
+ *
212
+ * It will iterate over all elements of the iterator checjing if they satisfy the condition.
213
+ * Once a single element of one group doesn't satisfy the condition,
214
+ * the result for the respective group will be `false`.
215
+ *
216
+ * Eventually, it will return a new {@link ReducedIterator}
217
+ * object that will contain all the boolean results for each group.
218
+ * If the iterator is infinite, the method will never return.
219
+ *
220
+ * ---
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
225
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
226
+ * .every(async (key, value) => value >= 0);
227
+ *
228
+ * console.log(await results.toObject()); // { odd: false, even: true }
229
+ * ```
230
+ *
231
+ * ---
232
+ *
233
+ * @param predicate The condition to check for each element of the iterator.
234
+ *
235
+ * @returns
236
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing the boolean results for each group.
237
+ */
30
238
  public async every(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>): Promise<ReducedIterator<K, boolean>>
31
239
  {
32
240
  const values = new Map<K, [number, boolean]>();
@@ -45,6 +253,38 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
45
253
  for (const [key, [_, result]] of values) { yield [key, result]; }
46
254
  });
47
255
  }
256
+
257
+ /**
258
+ * Determines whether any element of each group of the iterator satisfies a given condition.
259
+ * See also {@link AggregatedAsyncIterator.every}.
260
+ * This method will consume the entire iterator in the process.
261
+ *
262
+ * It will iterate over all elements of the iterator checjing if they satisfy the condition.
263
+ * Once a single element of one group satisfies the condition,
264
+ * the result for the respective group will be `true`.
265
+ *
266
+ * Eventually, it will return a new {@link ReducedIterator}
267
+ * object that will contain all the boolean results for each group.
268
+ * If the iterator is infinite, the method will never return.
269
+ *
270
+ * ---
271
+ *
272
+ * @example
273
+ * ```ts
274
+ * const results = new SmartAsyncIterator<number>([-5, -4, -3, -2, -1, 0])
275
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
276
+ * .some(async (key, value) => value >= 0);
277
+ *
278
+ * console.log(await results.toObject()); // { odd: false, even: true }
279
+ * ```
280
+ *
281
+ * ---
282
+ *
283
+ * @param predicate The condition to check for each element of the iterator.
284
+ *
285
+ * @returns
286
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing the boolean results for each group.
287
+ */
48
288
  public async some(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>): Promise<ReducedIterator<K, boolean>>
49
289
  {
50
290
  const values = new Map<K, [number, boolean]>();
@@ -64,8 +304,69 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
64
304
  });
65
305
  }
66
306
 
307
+ /**
308
+ * Filters the elements of the iterator based on a given condition.
309
+ *
310
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
311
+ * If the condition is met, the element will be included in the new iterator.
312
+ *
313
+ * A new iterator will be created, holding the reference to the original one.
314
+ * This means that the original iterator won't be consumed until the
315
+ * new one is and that consuming one of them will consume the other as well.
316
+ *
317
+ * ---
318
+ *
319
+ * @example
320
+ * ```ts
321
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
322
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
323
+ * .filter(async (key, value) => value >= 0);
324
+ *
325
+ * console.log(await results.toObject()); // { odd: [3, 5], even: [0, 2, 6, 8] }
326
+ * ```
327
+ *
328
+ * ---
329
+ *
330
+ * @param predicate The condition to check for each element of the iterator.
331
+ *
332
+ * @returns A new {@link AggregatedAsyncIterator} containing the elements that satisfy the condition.
333
+ */
67
334
  public filter(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>): AggregatedAsyncIterator<K, T>;
68
- public filter<S extends T>(predicate: MaybeAsyncKeyedTypeGuardIteratee<K, T, S>): AggregatedAsyncIterator<K, S>;
335
+
336
+ /**
337
+ * Filters the elements of the iterator based on a given condition.
338
+ *
339
+ * This method will iterate over all elements of the iterator checking if they satisfy the condition.
340
+ * If the condition is met, the element will be included in the new iterator.
341
+ *
342
+ * A new iterator will be created, holding the reference to the original one.
343
+ * This means that the original iterator won't be consumed until the
344
+ * new one is and that consuming one of them will consume the other as well.
345
+ *
346
+ * ---
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * const results = new SmartAsyncIterator<number>([-3, "-1", 0, "2", "3", 5, 6, "8"])
351
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
352
+ * .filter<number>(async (key, value) => typeof value === "number");
353
+ *
354
+ * console.log(await results.toObject()); // { odd: [-3, 5], even: [0, 6] }
355
+ * ```
356
+ *
357
+ * ---
358
+ *
359
+ * @template S
360
+ * The type of the elements that satisfy the condition.
361
+ * This allows the type-system to infer the correct type of the new iterator.
362
+ *
363
+ * It must be a subtype of the original type of the elements.
364
+ *
365
+ * @param predicate The type guard condition to check for each element of the iterator.
366
+ *
367
+ * @returns A new {@link AggregatedAsyncIterator} containing the elements that satisfy the condition.
368
+ */
369
+ public filter<S extends T>(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>): AggregatedAsyncIterator<K, S>;
69
370
  public filter(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>): AggregatedAsyncIterator<K, T>
70
371
  {
71
372
  const elements = this._elements;
@@ -73,17 +374,48 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
73
374
  return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[K, T]>
74
375
  {
75
376
  const indexes = new Map<K, number>();
76
-
77
377
  for await (const [key, element] of elements)
78
378
  {
79
379
  const index = indexes.get(key) ?? 0;
80
-
81
380
  if (await predicate(key, element, index)) { yield [key, element]; }
82
381
 
83
382
  indexes.set(key, index + 1);
84
383
  }
85
384
  });
86
385
  }
386
+
387
+ /**
388
+ * Maps the elements of the iterator using a given transformation function.
389
+ *
390
+ * This method will iterate over all elements of the iterator applying the condition.
391
+ * The result of each transformation will be included in the new iterator.
392
+ *
393
+ * Since the iterator is lazy, the mapping process will
394
+ * be executed once the resulting iterator is materialized.
395
+ *
396
+ * A new iterator will be created, holding the reference to the original one.
397
+ * This means that the original iterator won't be consumed until the
398
+ * new one is and that consuming one of them will consume the other as well.
399
+ *
400
+ * ---
401
+ *
402
+ * @example
403
+ * ```ts
404
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
405
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
406
+ * .map(async (key, value) => Math.abs(value));
407
+ *
408
+ * console.log(await results.toObject()); // { odd: [3, 1, 3, 5], even: [0, 2, 6, 8] }
409
+ * ```
410
+ *
411
+ * ---
412
+ *
413
+ * @template V The type of the elements after the transformation.
414
+ *
415
+ * @param iteratee The transformation function to apply to each element of the iterator.
416
+ *
417
+ * @returns A new {@link AggregatedAsyncIterator} containing the transformed elements.
418
+ */
87
419
  public map<V>(iteratee: MaybeAsyncKeyedIteratee<K, T, V>): AggregatedAsyncIterator<K, V>
88
420
  {
89
421
  const elements = this._elements;
@@ -91,22 +423,128 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
91
423
  return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[K, V]>
92
424
  {
93
425
  const indexes = new Map<K, number>();
94
-
95
426
  for await (const [key, element] of elements)
96
427
  {
97
428
  const index = indexes.get(key) ?? 0;
98
-
99
429
  yield [key, await iteratee(key, element, index)];
100
430
 
101
431
  indexes.set(key, index + 1);
102
432
  }
103
433
  });
104
434
  }
435
+
436
+ /**
437
+ * Reduces the elements of the iterator using a given reducer function.
438
+ * This method will consume the entire iterator in the process.
439
+ *
440
+ * It will iterate over all elements of the iterator applying the reducer function.
441
+ * The result of each iteration will be passed as the accumulator to the next one.
442
+ *
443
+ * The first accoumulator value will be the first element of the iterator.
444
+ * The last accumulator value will be the final result of the reduction.
445
+ *
446
+ * Eventually, it will return a new {@link ReducedIterator}
447
+ * object that will contain all the reduced results for each group.
448
+ * If the iterator is infinite, the method will never return.
449
+ *
450
+ * ---
451
+ *
452
+ * @example
453
+ * ```ts
454
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
455
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
456
+ * .reduce(async (key, accumulator, value) => accumulator + value);
457
+ *
458
+ * console.log(await results.toObject()); // { odd: 4, even: 16 }
459
+ * ```
460
+ *
461
+ * ---
462
+ *
463
+ * @param reducer The reducer function to apply to each element of the iterator.
464
+ *
465
+ * @returns
466
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing the reduced results for each group.
467
+ */
105
468
  public async reduce(reducer: MaybeAsyncKeyedReducer<K, T, T>): Promise<ReducedIterator<K, T>>;
469
+
470
+ /**
471
+ * Reduces the elements of the iterator using a given reducer function.
472
+ * This method will consume the entire iterator in the process.
473
+ *
474
+ * It will iterate over all elements of the iterator applying the reducer function.
475
+ * The result of each iteration will be passed as the accumulator to the next one.
476
+ *
477
+ * The first accoumulator value will be the provided initial value.
478
+ * The last accumulator value will be the final result of the reduction.
479
+ *
480
+ * Eventually, it will return a new {@link ReducedIterator}
481
+ * object that will contain all the reduced results for each group.
482
+ * If the iterator is infinite, the method will never return.
483
+ *
484
+ * ---
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
489
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
490
+ * .reduce(async (key, accumulator, value) => accumulator + value, 0);
491
+ *
492
+ * console.log(await results.toObject()); // { odd: 4, even: 16 }
493
+ * ```
494
+ *
495
+ * ---
496
+ *
497
+ * @template A The type of the accumulator value which will also be the final result of the reduction.
498
+ *
499
+ * @param reducer The reducer function to apply to each element of the iterator.
500
+ * @param initialValue The initial value for the accumulator.
501
+ *
502
+ * @returns
503
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing the reduced results for each group.
504
+ */
505
+ public async reduce<A extends PropertyKey>(reducer: MaybeAsyncKeyedReducer<K, T, A>, initialValue: MaybePromise<A>)
506
+ : Promise<ReducedIterator<K, A>>;
507
+
508
+ /**
509
+ * Reduces the elements of the iterator using a given reducer function.
510
+ * This method will consume the entire iterator in the process.
511
+ *
512
+ * It will iterate over all elements of the iterator applying the reducer function.
513
+ * The result of each iteration will be passed as the accumulator to the next one.
514
+ *
515
+ * The first accoumulator value will be the provided initial value by the given function.
516
+ * The last accumulator value will be the final result of the reduction.
517
+ *
518
+ * Eventually, it will return a new {@link ReducedIterator}
519
+ * object that will contain all the reduced results for each group.
520
+ * If the iterator is infinite, the method will never return.
521
+ *
522
+ * ---
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
527
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
528
+ * .reduce(async (key, { value }, currentValue) => ({ value: value + currentValue }), (key) => ({ value: 0 }));
529
+ *
530
+ * console.log(await results.toObject()); // { odd: { value: 4 }, even: { value: 16 } }
531
+ * ```
532
+ *
533
+ * ---
534
+ *
535
+ * @template A The type of the accumulator value which will also be the final result of the reduction.
536
+ *
537
+ * @param reducer The reducer function to apply to each element of the iterator.
538
+ * @param initialValue The function that provides the initial value for the accumulator.
539
+ *
540
+ * @returns
541
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing the reduced results for each group.
542
+ */
106
543
  public async reduce<A>(reducer: MaybeAsyncKeyedReducer<K, T, A>, initialValue: (key: K) => MaybePromise<A>)
107
544
  : Promise<ReducedIterator<K, A>>;
108
- public async reduce<A>(reducer: MaybeAsyncKeyedReducer<K, T, A>, initialValue?: (key: K) => MaybePromise<A>)
109
- : Promise<ReducedIterator<K, A>>
545
+ public async reduce<A>(
546
+ reducer: MaybeAsyncKeyedReducer<K, T, A>, initialValue?: MaybePromise<A> | ((key: K) => MaybePromise<A>)
547
+ ): Promise<ReducedIterator<K, A>>
110
548
  {
111
549
  const values = new Map<K, [number, A]>();
112
550
 
@@ -119,7 +557,9 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
119
557
  else if (initialValue !== undefined)
120
558
  {
121
559
  index = 0;
122
- accumulator = await initialValue(key);
560
+
561
+ if (initialValue instanceof Function) { accumulator = await initialValue(key); }
562
+ else { accumulator = await initialValue; }
123
563
  }
124
564
  else
125
565
  {
@@ -137,26 +577,94 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
137
577
  });
138
578
  }
139
579
 
140
- public flatMap<V>(iteratee: MaybeAsyncKeyedIteratee<K, T, Iterable<V>>): AggregatedAsyncIterator<K, V>
580
+ /**
581
+ * Flattens the elements of the iterator using a given transformation function.
582
+ *
583
+ * This method will iterate over all elements of the iterator applying the transformation function.
584
+ * The result of each transformation will be included in the new iterator.
585
+ *
586
+ * Since the iterator is lazy, the mapping process will
587
+ * be executed once the resulting iterator is materialized.
588
+ *
589
+ * A new iterator will be created, holding the reference to the original one.
590
+ * This means that the original iterator won't be consumed until the
591
+ * new one is and that consuming one of them will consume the other as well.
592
+ *
593
+ * ---
594
+ *
595
+ * @example
596
+ * ```ts
597
+ * const results = new SmartAsyncIterator<number>([[-3, -1], 0, 2, 3, 5, [6, 8]])
598
+ * .groupBy(async (values) =>
599
+ * {
600
+ * const value = values instanceof Array ? values[0] : values;
601
+ * return value % 2 === 0 ? "even" : "odd";
602
+ * })
603
+ * .flatMap(async (key, values) => values);
604
+ *
605
+ * console.log(await results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] }
606
+ * ```
607
+ *
608
+ * ---
609
+ *
610
+ * @template V The type of the elements after the transformation.
611
+ *
612
+ * @param iteratee The transformation function to apply to each element of the iterator.
613
+ *
614
+ * @returns A new {@link AggregatedAsyncIterator} containing the transformed elements.
615
+ */
616
+ public flatMap<V>(iteratee: MaybeAsyncKeyedIteratee<K, T, V | readonly V[]>): AggregatedAsyncIterator<K, V>
141
617
  {
142
618
  const elements = this._elements;
143
619
 
144
620
  return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[K, V]>
145
621
  {
146
622
  const indexes = new Map<K, number>();
147
-
148
623
  for await (const [key, element] of elements)
149
624
  {
150
625
  const index = indexes.get(key) ?? 0;
151
626
  const values = await iteratee(key, element, index);
152
627
 
153
- for await (const value of values) { yield [key, value]; }
628
+ if (values instanceof Array)
629
+ {
630
+ for (const value of values) { yield [key, value]; }
631
+ }
632
+ else { yield [key, values]; }
154
633
 
155
634
  indexes.set(key, index + 1);
156
635
  }
157
636
  });
158
637
  }
159
638
 
639
+ /**
640
+ * Drops a given number of elements from the beginning of each group of the iterator.
641
+ * The remaining elements will be included in the new iterator.
642
+ * See also {@link AggregatedAsyncIterator.take}.
643
+ *
644
+ * Since the iterator is lazy, the dropping process will
645
+ * be executed once the resulting iterator is materialized.
646
+ *
647
+ * A new iterator will be created, holding the reference to the original one.
648
+ * This means that the original iterator won't be consumed until the
649
+ * new one is and that consuming one of them will consume the other as well.
650
+ *
651
+ * ---
652
+ *
653
+ * @example
654
+ * ```ts
655
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
656
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
657
+ * .drop(2);
658
+ *
659
+ * console.log(await results.toObject()); // { odd: [3, 5], even: [6, 8] }
660
+ * ```
661
+ *
662
+ * ---
663
+ *
664
+ * @param count The number of elements to drop from the beginning of each group.
665
+ *
666
+ * @returns A new {@link AggregatedAsyncIterator} containing the remaining elements.
667
+ */
160
668
  public drop(count: number): AggregatedAsyncIterator<K, T>
161
669
  {
162
670
  const elements = this._elements;
@@ -164,7 +672,6 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
164
672
  return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[K, T]>
165
673
  {
166
674
  const indexes = new Map<K, number>();
167
-
168
675
  for await (const [key, element] of elements)
169
676
  {
170
677
  const index = indexes.get(key) ?? 0;
@@ -179,6 +686,36 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
179
686
  }
180
687
  });
181
688
  }
689
+
690
+ /**
691
+ * Takes a given number of elements from the beginning of each group of the iterator.
692
+ * The elements will be included in the new iterator.
693
+ * See also {@link AggregatedAsyncIterator.drop}.
694
+ *
695
+ * Since the iterator is lazy, the taking process will
696
+ * be executed once the resulting iterator is materialized.
697
+ *
698
+ * A new iterator will be created, holding the reference to the original one.
699
+ * This means that the original iterator won't be consumed until the
700
+ * new one is and that consuming one of them will consume the other as well.
701
+ *
702
+ * ---
703
+ *
704
+ * @example
705
+ * ```ts
706
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
707
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
708
+ * .take(2);
709
+ *
710
+ * console.log(await results.toObject()); // { odd: [-3, -1], even: [0, 2] }
711
+ * ```
712
+ *
713
+ * ---
714
+ *
715
+ * @param limit The number of elements to take from the beginning of each group.
716
+ *
717
+ * @returns A new {@link AggregatedAsyncIterator} containing the taken elements.
718
+ */
182
719
  public take(limit: number): AggregatedAsyncIterator<K, T>
183
720
  {
184
721
  const elements = this._elements;
@@ -186,7 +723,6 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
186
723
  return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[K, T]>
187
724
  {
188
725
  const indexes = new Map<K, number>();
189
-
190
726
  for await (const [key, element] of elements)
191
727
  {
192
728
  const index = indexes.get(key) ?? 0;
@@ -199,8 +735,77 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
199
735
  });
200
736
  }
201
737
 
738
+ /**
739
+ * Finds the first element of each group of the iterator that satisfies a given condition.
740
+ * This method will consume the entire iterator in the process.
741
+ *
742
+ * It will iterate over all elements of the iterator checking if they satisfy the condition.
743
+ * Once the first element of one group satisfies the condition,
744
+ * the result for the respective group will be the element itself.
745
+ *
746
+ * Eventually, it will return a new {@link ReducedIterator}
747
+ * object that will contain the first element that satisfies the condition for each group.
748
+ * If the iterator is infinite, the method will never return.
749
+ *
750
+ * ---
751
+ *
752
+ * @example
753
+ * ```ts
754
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
755
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
756
+ * .find(async (key, value) => value > 0);
757
+ *
758
+ * console.log(await results.toObject()); // { odd: 3, even: 2 }
759
+ * ```
760
+ *
761
+ * ---
762
+ *
763
+ * @param predicate The condition to check for each element of the iterator.
764
+ *
765
+ * @returns
766
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing
767
+ * the first element that satisfies the condition for each group.
768
+ */
202
769
  public async find(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>): Promise<ReducedIterator<K, T | undefined>>;
203
- public async find<S extends T>(predicate: MaybeAsyncKeyedTypeGuardIteratee<K, T, S>)
770
+
771
+ /**
772
+ * Finds the first element of each group of the iterator that satisfies a given condition.
773
+ * This method will consume the entire iterator in the process.
774
+ *
775
+ * It will iterate over all elements of the iterator checking if they satisfy the condition.
776
+ * Once the first element of one group satisfies the condition,
777
+ * the result for the respective group will be the element itself.
778
+ *
779
+ * Eventually, it will return a new {@link ReducedIterator}
780
+ * object that will contain the first element that satisfies the condition for each group.
781
+ * If the iterator is infinite, the method will never return.
782
+ *
783
+ * ---
784
+ *
785
+ * @example
786
+ * ```ts
787
+ * const results = new SmartAsyncIterator<number | string>([-3, "-1", 0, "2", "3", 5, 6, "8"])
788
+ * .groupBy(async (value) => Number(value) % 2 === 0 ? "even" : "odd")
789
+ * .find<number>(async (key, value) => typeof value === "number");
790
+ *
791
+ * console.log(await results.toObject()); // { odd: -3, even: 0 }
792
+ * ```
793
+ *
794
+ * ---
795
+ *
796
+ * @template S
797
+ * The type of the elements that satisfy the condition.
798
+ * This allows the type-system to infer the correct type of the new iterator.
799
+ *
800
+ * It must be a subtype of the original type of the elements.
801
+ *
802
+ * @param predicate The type guard condition to check for each element of the iterator.
803
+ *
804
+ * @returns
805
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing
806
+ * the first element that satisfies the condition for each group.
807
+ */
808
+ public async find<S extends T>(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>)
204
809
  : Promise<ReducedIterator<K, S | undefined>>;
205
810
 
206
811
  public async find(predicate: MaybeAsyncKeyedIteratee<K, T, boolean>): Promise<ReducedIterator<K, T | undefined>>
@@ -223,6 +828,63 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
223
828
  });
224
829
  }
225
830
 
831
+ /**
832
+ * Enumerates the elements of the iterator.
833
+ * Each element is paired with its index within the group in the new iterator.
834
+ *
835
+ * Since the iterator is lazy, the enumeration process will
836
+ * be executed once the resulting iterator is materialized.
837
+ *
838
+ * A new iterator will be created, holding the reference to the original one.
839
+ * This means that the original iterator won't be consumed until the
840
+ * new one is and that consuming one of them will consume the other as well.
841
+ *
842
+ * ---
843
+ *
844
+ * @example
845
+ * ```ts
846
+ * const results = new SmartAsyncIterator<number>([-3, 0, 2, -1, 3])
847
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
848
+ * .enumerate();
849
+ *
850
+ * console.log(results.toObject()); // { odd: [[0, -3], [1, -1], [2, 3]], even: [[0, 0], [1, 2]] }
851
+ * ```
852
+ *
853
+ * ---
854
+ *
855
+ * @returns A new {@link AggregatedAsyncIterator} containing the enumerated elements.
856
+ */
857
+ public enumerate(): AggregatedAsyncIterator<K, [number, T]>
858
+ {
859
+ return this.map((key, value, index) => [index, value]);
860
+ }
861
+
862
+ /**
863
+ * Removes all duplicate elements from within each group of the iterator.
864
+ * The first occurrence of each element will be included in the new iterator.
865
+ *
866
+ * Since the iterator is lazy, the deduplication process will
867
+ * be executed once the resulting iterator is materialized.
868
+ *
869
+ * A new iterator will be created, holding the reference to the original one.
870
+ * This means that the original iterator won't be consumed until the
871
+ * new one is and that consuming one of them will consume the other as well.
872
+ *
873
+ * ---
874
+ *
875
+ * @example
876
+ * ```ts
877
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 6, -3, -1, 0, 5, 6, 8, 0, 2])
878
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
879
+ * .unique();
880
+ *
881
+ * console.log(await results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] }
882
+ * ```
883
+ *
884
+ * ---
885
+ *
886
+ * @returns A new {@link AggregatedAsyncIterator} containing only the unique elements.
887
+ */
226
888
  public unique(): AggregatedAsyncIterator<K, T>
227
889
  {
228
890
  const elements = this._elements;
@@ -230,11 +892,9 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
230
892
  return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[K, T]>
231
893
  {
232
894
  const keys = new Map<K, Set<T>>();
233
-
234
895
  for await (const [key, element] of elements)
235
896
  {
236
897
  const values = keys.get(key) ?? new Set<T>();
237
-
238
898
  if (values.has(element)) { continue; }
239
899
 
240
900
  values.add(element);
@@ -245,6 +905,28 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
245
905
  });
246
906
  }
247
907
 
908
+ /**
909
+ * Counts the number of elements within each group of the iterator.
910
+ * This method will consume the entire iterator in the process.
911
+ *
912
+ * If the iterator is infinite, the method will never return.
913
+ *
914
+ * ---
915
+ *
916
+ * @example
917
+ * ```ts
918
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
919
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
920
+ * .count();
921
+ *
922
+ * console.log(await results.toObject()); // { odd: 4, even: 4 }
923
+ * ```
924
+ *
925
+ * ---
926
+ *
927
+ * @returns
928
+ * A {@link Promise} resolving to a new {@link ReducedIterator} containing the number of elements for each group.
929
+ */
248
930
  public async count(): Promise<ReducedIterator<K, number>>
249
931
  {
250
932
  const counters = new Map<K, number>();
@@ -262,6 +944,32 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
262
944
  });
263
945
  }
264
946
 
947
+ /**
948
+ * Iterates over the elements of the iterator.
949
+ * The elements are passed to the given iteratee function along with their key and index within the group.
950
+ *
951
+ * This method will consume the entire iterator in the process.
952
+ * If the iterator is infinite, the method will never return.
953
+ *
954
+ * ---
955
+ *
956
+ * @example
957
+ * ```ts
958
+ * const aggregator = new SmartAsyncIterator<number>([-3, 0, 2, -1, 3])
959
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd");
960
+ *
961
+ * await aggregator.forEach(async (key, value, index) =>
962
+ * {
963
+ * console.log(`${index}: ${value}`); // "0: -3", "0: 0", "1: 2", "1: -1", "2: 3"
964
+ * };
965
+ * ```
966
+ *
967
+ * ---
968
+ *
969
+ * @param iteratee The function to execute for each element of the iterator.
970
+ *
971
+ * @returns A {@link Promise} that will resolve once the iteration is complete.
972
+ */
265
973
  public async forEach(iteratee: MaybeAsyncKeyedIteratee<K, T>): Promise<void>
266
974
  {
267
975
  const indexes = new Map<K, number>();
@@ -270,12 +978,87 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
270
978
  {
271
979
  const index = indexes.get(key) ?? 0;
272
980
 
273
- iteratee(key, element, index);
981
+ await iteratee(key, element, index);
274
982
 
275
983
  indexes.set(key, index + 1);
276
984
  }
277
985
  }
278
986
 
987
+ /**
988
+ * Changes the key of each element on which the iterator is aggregated.
989
+ * The new key is determined by the given iteratee function.
990
+ *
991
+ * Since the iterator is lazy, the reorganization process will
992
+ * be executed once the resulting iterator is materialized.
993
+ *
994
+ * A new iterator will be created, holding the reference to the original one.
995
+ * This means that the original iterator won't be consumed until the
996
+ * new one is and that consuming one of them will consume the other as well.
997
+ *
998
+ * ---
999
+ *
1000
+ * @example
1001
+ * ```ts
1002
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
1003
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
1004
+ * .map(async (key, value, index) => index % 2 === 0 ? value : -value)
1005
+ * .reorganizeBy(async (key, value) => value >= 0 ? "+" : "-");
1006
+ *
1007
+ * console.log(await results.toObject()); // { "+": [1, 0, 3, 6], "-": [-3, -2, -5, -8] }
1008
+ * ```
1009
+ *
1010
+ * ---
1011
+ *
1012
+ * @template J The type of the new key.
1013
+ *
1014
+ * @param iteratee The function to determine the new key for each element of the iterator.
1015
+ *
1016
+ * @returns A new {@link AggregatedAsyncIterator} containing the elements reorganized by the new keys.
1017
+ */
1018
+ public reorganizeBy<J extends PropertyKey>(iteratee: MaybeAsyncKeyedIteratee<K, T, J>)
1019
+ : AggregatedAsyncIterator<J, T>
1020
+ {
1021
+ const elements = this._elements;
1022
+
1023
+ return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[J, T]>
1024
+ {
1025
+ const indexes = new Map<K, number>();
1026
+ for await (const [key, element] of elements)
1027
+ {
1028
+ const index = indexes.get(key) ?? 0;
1029
+ yield [await iteratee(key, element, index), element];
1030
+
1031
+ indexes.set(key, index + 1);
1032
+ }
1033
+ });
1034
+ }
1035
+
1036
+ /**
1037
+ * An utility method that returns a new {@link SmartAsyncIterator}
1038
+ * object containing all the keys of the iterator.
1039
+ *
1040
+ * Since the iterator is lazy, the keys will be extracted
1041
+ * be executed once the resulting iterator is materialized.
1042
+ *
1043
+ * A new iterator will be created, holding the reference to the original one.
1044
+ * This means that the original iterator won't be consumed until the
1045
+ * new one is and that consuming one of them will consume the other as well.
1046
+ *
1047
+ * ---
1048
+ *
1049
+ * @example
1050
+ * ```ts
1051
+ * const keys = new SmartAsyncIterator([-3, Symbol(), "A", { }, null, [1 , 2, 3], false])
1052
+ * .groupBy(async (value) => typeof value)
1053
+ * .keys();
1054
+ *
1055
+ * console.log(await keys.toArray()); // ["number", "symbol", "string", "object", "boolean"]
1056
+ * ```
1057
+ *
1058
+ * ---
1059
+ *
1060
+ * @returns A new {@link SmartAsyncIterator} containing all the keys of the iterator.
1061
+ */
279
1062
  public keys(): SmartAsyncIterator<K>
280
1063
  {
281
1064
  const elements = this._elements;
@@ -283,7 +1066,6 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
283
1066
  return new SmartAsyncIterator<K>(async function* ()
284
1067
  {
285
1068
  const keys = new Set<K>();
286
-
287
1069
  for await (const [key] of elements)
288
1070
  {
289
1071
  if (keys.has(key)) { continue; }
@@ -293,10 +1075,65 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
293
1075
  }
294
1076
  });
295
1077
  }
296
- public items(): SmartAsyncIterator<[K, T]>
1078
+
1079
+ /**
1080
+ * An utility method that returns a new {@link SmartAsyncIterator}
1081
+ * object containing all the entries of the iterator.
1082
+ * Each entry is a tuple containing the key and the element.
1083
+ *
1084
+ * Since the iterator is lazy, the entries will be extracted
1085
+ * be executed once the resulting iterator is materialized.
1086
+ *
1087
+ * A new iterator will be created, holding the reference to the original one.
1088
+ * This means that the original iterator won't be consumed until the
1089
+ * new one is and that consuming one of them will consume the other as well.
1090
+ *
1091
+ * ---
1092
+ *
1093
+ * @example
1094
+ * ```ts
1095
+ * const entries = new SmartAsyncIterator<number>([-3, 0, 2, -1, 3])
1096
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
1097
+ * .entries();
1098
+ *
1099
+ * console.log(await entries.toArray()); // [["odd", -3], ["even", 0], ["even", 2], ["odd", -1], ["odd", 3]]
1100
+ * ```
1101
+ *
1102
+ * ---
1103
+ *
1104
+ * @returns A new {@link SmartAsyncIterator} containing all the entries of the iterator.
1105
+ */
1106
+ public entries(): SmartAsyncIterator<[K, T]>
297
1107
  {
298
1108
  return this._elements;
299
1109
  }
1110
+
1111
+ /**
1112
+ * An utility method that returns a new {@link SmartAsyncIterator}
1113
+ * object containing all the values of the iterator.
1114
+ *
1115
+ * Since the iterator is lazy, the values will be extracted
1116
+ * be executed once the resulting iterator is materialized.
1117
+ *
1118
+ * A new iterator will be created, holding the reference to the original one.
1119
+ * This means that the original iterator won't be consumed until the
1120
+ * new one is and that consuming one of them will consume the other as well.
1121
+ *
1122
+ * ---
1123
+ *
1124
+ * @example
1125
+ * ```ts
1126
+ * const values = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
1127
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
1128
+ * .values();
1129
+ *
1130
+ * console.log(await values.toArray()); // [-3, -1, 0, 2, 3, 5, 6, 8]
1131
+ * ```
1132
+ *
1133
+ * ---
1134
+ *
1135
+ * @returns A new {@link SmartAsyncIterator} containing all the values of the iterator.
1136
+ */
300
1137
  public values(): SmartAsyncIterator<T>
301
1138
  {
302
1139
  const elements = this._elements;
@@ -307,12 +1144,53 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
307
1144
  });
308
1145
  }
309
1146
 
1147
+ /**
1148
+ * Materializes the iterator into an array of arrays.
1149
+ * This method will consume the entire iterator in the process.
1150
+ *
1151
+ * If the iterator is infinite, the method will never return.
1152
+ *
1153
+ * ---
1154
+ *
1155
+ * @example
1156
+ * ```ts
1157
+ * const aggregator = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
1158
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd");
1159
+ *
1160
+ * console.log(await aggregator.toArray()); // [[-3, -1, 3, 5], [0, 2, 6, 8]]
1161
+ * ```
1162
+ *
1163
+ * ---
1164
+ *
1165
+ * @returns A {@link Promise} resolving to an {@link Array} containing all the values of the iterator.
1166
+ */
310
1167
  public async toArray(): Promise<T[][]>
311
1168
  {
312
1169
  const map = await this.toMap();
313
1170
 
314
1171
  return Array.from(map.values());
315
1172
  }
1173
+
1174
+ /**
1175
+ * Materializes the iterator into a map.
1176
+ * This method will consume the entire iterator in the process.
1177
+ *
1178
+ * If the iterator is infinite, the method will never return.
1179
+ *
1180
+ * ---
1181
+ *
1182
+ * @example
1183
+ * ```ts
1184
+ * const aggregator = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
1185
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd");
1186
+ *
1187
+ * console.log(await aggregator.toMap()); // Map(2) { "odd" => [-3, -1, 3, 5], "even" => [0, 2, 6, 8] }
1188
+ * ```
1189
+ *
1190
+ * ---
1191
+ *
1192
+ * @returns A {@link Promise} resolving to a {@link Map} containing all the entries of the iterator.
1193
+ */
316
1194
  public async toMap(): Promise<Map<K, T[]>>
317
1195
  {
318
1196
  const groups = new Map<K, T[]>();
@@ -327,6 +1205,27 @@ export default class AggregatedAsyncIterator<K extends PropertyKey, T>
327
1205
 
328
1206
  return groups;
329
1207
  }
1208
+
1209
+ /**
1210
+ * Materializes the iterator into an object.
1211
+ * This method will consume the entire iterator in the process.
1212
+ *
1213
+ * If the iterator is infinite, the method will never return.
1214
+ *
1215
+ * ---
1216
+ *
1217
+ * @example
1218
+ * ```ts
1219
+ * const aggregator = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
1220
+ * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd");
1221
+ *
1222
+ * console.log(await aggregator.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] }
1223
+ * ```
1224
+ *
1225
+ * ---
1226
+ *
1227
+ * @returns A {@link Promise} resolving to an object containing all the entries of the iterator.
1228
+ */
330
1229
  public async toObject(): Promise<Record<K, T[]>>
331
1230
  {
332
1231
  const groups = { } as Record<K, T[]>;