@mark1russell7/collections 0.0.0-stage → 0.2.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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -2
  3. package/build/dag/index.d.ts +144 -0
  4. package/build/dag/index.d.ts.map +1 -0
  5. package/build/dag/index.js +294 -0
  6. package/build/dag/index.js.map +1 -0
  7. package/build/graph/index.d.ts +20 -0
  8. package/build/graph/index.d.ts.map +1 -0
  9. package/build/graph/index.js +48 -0
  10. package/build/graph/index.js.map +1 -0
  11. package/build/index.d.ts +4967 -0
  12. package/build/index.d.ts.map +1 -0
  13. package/build/index.js +7897 -0
  14. package/build/index.js.map +1 -0
  15. package/build/testing/index.d.ts +25 -0
  16. package/build/testing/index.d.ts.map +1 -0
  17. package/build/testing/index.js +72 -0
  18. package/build/testing/index.js.map +1 -0
  19. package/package.json +77 -3
  20. package/src/async/async-queue.ts +481 -0
  21. package/src/async/channels.ts +450 -0
  22. package/src/async/settled.ts +41 -0
  23. package/src/behaviors/bounded.ts +485 -0
  24. package/src/behaviors/composite.ts +99 -0
  25. package/src/behaviors/evented.ts +464 -0
  26. package/src/behaviors/lru.ts +449 -0
  27. package/src/behaviors/readonly.ts +236 -0
  28. package/src/behaviors/safe.ts +358 -0
  29. package/src/behaviors/synchronized.ts +316 -0
  30. package/src/behaviors/ttl.ts +464 -0
  31. package/src/core/effects.ts +327 -0
  32. package/src/core/events.ts +363 -0
  33. package/src/core/middleware.ts +413 -0
  34. package/src/core/policies.ts +316 -0
  35. package/src/core/traits.ts +164 -0
  36. package/src/dag/builder.ts +124 -0
  37. package/src/dag/executor.ts +190 -0
  38. package/src/dag/index.ts +14 -0
  39. package/src/dag/traversal.ts +129 -0
  40. package/src/dag/types.ts +87 -0
  41. package/src/fx/collectors.ts +500 -0
  42. package/src/fx/iter.ts +536 -0
  43. package/src/graph/hub-order.ts +53 -0
  44. package/src/graph/index.ts +5 -0
  45. package/src/impl/array-deque.ts +463 -0
  46. package/src/impl/array-list.ts +490 -0
  47. package/src/impl/hash-map.ts +623 -0
  48. package/src/impl/hash-set.ts +342 -0
  49. package/src/impl/linked-hash-map.ts +572 -0
  50. package/src/impl/linked-list.ts +610 -0
  51. package/src/impl/priority-queue.ts +448 -0
  52. package/src/impl/summary-tree.ts +594 -0
  53. package/src/impl/tree-map.ts +913 -0
  54. package/src/impl/tree-set.ts +469 -0
  55. package/src/index.ts +237 -0
  56. package/src/interfaces/collection.ts +141 -0
  57. package/src/interfaces/list.ts +281 -0
  58. package/src/interfaces/map.ts +405 -0
  59. package/src/interfaces/queue.ts +353 -0
  60. package/src/interfaces/set.ts +158 -0
  61. package/src/order/index.ts +6 -0
  62. package/src/order/rank.ts +271 -0
  63. package/src/storage/listeners.ts +72 -0
  64. package/src/testing/check.ts +81 -0
  65. package/src/testing/index.ts +7 -0
  66. package/src/utils/defaults.ts +443 -0
  67. package/src/utils/factories.ts +357 -0
  68. package/src/utils/helpers.ts +248 -0
package/src/fx/iter.ts ADDED
@@ -0,0 +1,536 @@
1
+ /**
2
+ * Functional operations for iterables (lazy sync transforms).
3
+ *
4
+ * This module gives Stream-like operations with lazy evaluation.
5
+ * All operations return generators for composability.
6
+ *
7
+ * @example
8
+ * const result = pipe(
9
+ * [1, 2, 3, 4, 5],
10
+ * map(x => x * 2),
11
+ * filter(x => x > 5),
12
+ * take(2),
13
+ * toArray
14
+ * ) // [6, 8]
15
+ */
16
+
17
+ // ============================================================================
18
+ // Mapping and transformation
19
+ // ============================================================================
20
+
21
+ /**
22
+ * This function maps each element through a function.
23
+ *
24
+ * @example
25
+ * const doubled = map([1, 2, 3], x => x * 2) // [2, 4, 6]
26
+ */
27
+ export function* map<T, U>(
28
+ iterable: Iterable<T>,
29
+ fn: (value: T, index: number) => U
30
+ ): Generator<U> {
31
+ let index = 0;
32
+ for (const value of iterable) {
33
+ yield fn(value, index++);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * This function filters elements based on a predicate.
39
+ */
40
+ export function* filter<T>(
41
+ iterable: Iterable<T>,
42
+ predicate: (value: T, index: number) => boolean
43
+ ): Generator<T> {
44
+ let index = 0;
45
+ for (const value of iterable) {
46
+ if (predicate(value, index++)) {
47
+ yield value;
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * This function flatMaps each element to an iterable and flattens.
54
+ */
55
+ export function* flatMap<T, U>(
56
+ iterable: Iterable<T>,
57
+ fn: (value: T, index: number) => Iterable<U>
58
+ ): Generator<U> {
59
+ let index = 0;
60
+ for (const value of iterable) {
61
+ yield* fn(value, index++);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * This function flattens a nested iterable.
67
+ */
68
+ export function* flatten<T>(iterable: Iterable<Iterable<T>>): Generator<T> {
69
+ for (const inner of iterable) {
70
+ yield* inner;
71
+ }
72
+ }
73
+
74
+ // ============================================================================
75
+ // Limiting and skipping
76
+ // ============================================================================
77
+
78
+ /**
79
+ * This function takes the first n elements.
80
+ */
81
+ export function* take<T>(iterable: Iterable<T>, n: number): Generator<T> {
82
+ let count = 0;
83
+ for (const value of iterable) {
84
+ if (count++ >= n) break;
85
+ yield value;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * This function skips the first n elements.
91
+ */
92
+ export function* skip<T>(iterable: Iterable<T>, n: number): Generator<T> {
93
+ let count = 0;
94
+ for (const value of iterable) {
95
+ if (count++ >= n) {
96
+ yield value;
97
+ }
98
+ }
99
+ }
100
+
101
+ /**
102
+ * This function takes elements while predicate is true.
103
+ */
104
+ export function* takeWhile<T>(
105
+ iterable: Iterable<T>,
106
+ predicate: (value: T) => boolean
107
+ ): Generator<T> {
108
+ for (const value of iterable) {
109
+ if (!predicate(value)) break;
110
+ yield value;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * This function skips elements while predicate is true.
116
+ */
117
+ export function* skipWhile<T>(
118
+ iterable: Iterable<T>,
119
+ predicate: (value: T) => boolean
120
+ ): Generator<T> {
121
+ let skipping = true;
122
+ for (const value of iterable) {
123
+ if (skipping && predicate(value)) continue;
124
+ skipping = false;
125
+ yield value;
126
+ }
127
+ }
128
+
129
+ // ============================================================================
130
+ // Combining
131
+ // ============================================================================
132
+
133
+ /**
134
+ * Concatenates multiple iterables.
135
+ */
136
+ export function* concat<T>(...iterables: Iterable<T>[]): Generator<T> {
137
+ for (const iterable of iterables) {
138
+ yield* iterable;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Zips multiple iterables together.
144
+ */
145
+ export function* zip<T extends any[]>(
146
+ ...iterables: { [K in keyof T]: Iterable<T[K]> }
147
+ ): Generator<T> {
148
+ const iterators = iterables.map((it) => it[Symbol.iterator]());
149
+
150
+ while (true) {
151
+ const results = iterators.map((it) => it.next());
152
+ if (results.some((r) => r.done)) break;
153
+ yield results.map((r) => r.value) as T;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Zips with index.
159
+ */
160
+ export function* enumerate<T>(iterable: Iterable<T>): Generator<[number, T]> {
161
+ let index = 0;
162
+ for (const value of iterable) {
163
+ yield [index++, value];
164
+ }
165
+ }
166
+
167
+ // ============================================================================
168
+ // Partitioning
169
+ // ============================================================================
170
+
171
+ /**
172
+ * Chunks elements into arrays of specified size.
173
+ */
174
+ export function* chunk<T>(iterable: Iterable<T>, size: number): Generator<T[]> {
175
+ let chunk: T[] = [];
176
+ for (const value of iterable) {
177
+ chunk.push(value);
178
+ if (chunk.length === size) {
179
+ yield chunk;
180
+ chunk = [];
181
+ }
182
+ }
183
+ if (chunk.length > 0) {
184
+ yield chunk;
185
+ }
186
+ }
187
+
188
+ /**
189
+ * This function makes a sliding window of specified size.
190
+ */
191
+ export function* window<T>(iterable: Iterable<T>, size: number): Generator<T[]> {
192
+ const window: T[] = [];
193
+ for (const value of iterable) {
194
+ window.push(value);
195
+ if (window.length > size) {
196
+ window.shift();
197
+ }
198
+ if (window.length === size) {
199
+ yield [...window];
200
+ }
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Partitions elements into two arrays based on predicate.
206
+ */
207
+ export function partition<T>(
208
+ iterable: Iterable<T>,
209
+ predicate: (value: T) => boolean
210
+ ): [T[], T[]] {
211
+ const truthy: T[] = [];
212
+ const falsy: T[] = [];
213
+
214
+ for (const value of iterable) {
215
+ if (predicate(value)) {
216
+ truthy.push(value);
217
+ } else {
218
+ falsy.push(value);
219
+ }
220
+ }
221
+
222
+ return [truthy, falsy];
223
+ }
224
+
225
+ // ============================================================================
226
+ // Reducing and folding
227
+ // ============================================================================
228
+
229
+ /**
230
+ * This function reduces iterable to a single value.
231
+ */
232
+ export function reduce<T, U>(
233
+ iterable: Iterable<T>,
234
+ fn: (acc: U, value: T, index: number) => U,
235
+ initial: U
236
+ ): U {
237
+ let acc = initial;
238
+ let index = 0;
239
+ for (const value of iterable) {
240
+ acc = fn(acc, value, index++);
241
+ }
242
+ return acc;
243
+ }
244
+
245
+ /**
246
+ * Scans (like reduce but emits intermediate results).
247
+ */
248
+ export function* scan<T, U>(
249
+ iterable: Iterable<T>,
250
+ fn: (acc: U, value: T) => U,
251
+ initial: U
252
+ ): Generator<U> {
253
+ let acc = initial;
254
+ yield acc;
255
+ for (const value of iterable) {
256
+ acc = fn(acc, value);
257
+ yield acc;
258
+ }
259
+ }
260
+
261
+ // ============================================================================
262
+ // Sorting and ordering
263
+ // ============================================================================
264
+
265
+ /**
266
+ * This function sorts elements (materializes the iterable).
267
+ */
268
+ export function* sort<T>(
269
+ iterable: Iterable<T>,
270
+ compare?: (a: T, b: T) => number
271
+ ): Generator<T> {
272
+ const array = Array.from(iterable);
273
+ array.sort(compare);
274
+ yield* array;
275
+ }
276
+
277
+ /**
278
+ * Reverses elements (materializes the iterable).
279
+ */
280
+ export function* reverse<T>(iterable: Iterable<T>): Generator<T> {
281
+ const array = Array.from(iterable);
282
+ array.reverse();
283
+ yield* array;
284
+ }
285
+
286
+ // ============================================================================
287
+ // Distinct and uniqueness
288
+ // ============================================================================
289
+
290
+ /**
291
+ * This function removes duplicate elements (by reference or key function).
292
+ */
293
+ export function* distinct<T, K = T>(
294
+ iterable: Iterable<T>,
295
+ keyFn?: (value: T) => K
296
+ ): Generator<T> {
297
+ const seen = new Set<K>();
298
+ for (const value of iterable) {
299
+ const key = (keyFn ? keyFn(value) : value) as K;
300
+ if (!seen.has(key)) {
301
+ seen.add(key);
302
+ yield value;
303
+ }
304
+ }
305
+ }
306
+
307
+ /**
308
+ * This function removes consecutive duplicates.
309
+ */
310
+ export function* distinctConsecutive<T, K = T>(
311
+ iterable: Iterable<T>,
312
+ keyFn?: (value: T) => K
313
+ ): Generator<T> {
314
+ let lastKey: K | undefined;
315
+ let first = true;
316
+
317
+ for (const value of iterable) {
318
+ const key = (keyFn ? keyFn(value) : value) as K;
319
+ if (first || key !== lastKey) {
320
+ yield value;
321
+ lastKey = key;
322
+ first = false;
323
+ }
324
+ }
325
+ }
326
+
327
+ // ============================================================================
328
+ // Peeking and side effects
329
+ // ============================================================================
330
+
331
+ /**
332
+ * This function does a side effect for each element. The stream does not change.
333
+ */
334
+ export function* tap<T>(
335
+ iterable: Iterable<T>,
336
+ fn: (value: T, index: number) => void
337
+ ): Generator<T> {
338
+ let index = 0;
339
+ for (const value of iterable) {
340
+ fn(value, index++);
341
+ yield value;
342
+ }
343
+ }
344
+
345
+ // ============================================================================
346
+ // Predicates and checks
347
+ // ============================================================================
348
+
349
+ /**
350
+ * This function gives true if any element matches the predicate.
351
+ */
352
+ export function some<T>(
353
+ iterable: Iterable<T>,
354
+ predicate: (value: T) => boolean
355
+ ): boolean {
356
+ for (const value of iterable) {
357
+ if (predicate(value)) return true;
358
+ }
359
+ return false;
360
+ }
361
+
362
+ /**
363
+ * This function gives true if all elements match the predicate.
364
+ */
365
+ export function every<T>(
366
+ iterable: Iterable<T>,
367
+ predicate: (value: T) => boolean
368
+ ): boolean {
369
+ for (const value of iterable) {
370
+ if (!predicate(value)) return false;
371
+ }
372
+ return true;
373
+ }
374
+
375
+ /**
376
+ * This function gives true if no elements match the predicate.
377
+ */
378
+ export function none<T>(
379
+ iterable: Iterable<T>,
380
+ predicate: (value: T) => boolean
381
+ ): boolean {
382
+ return !some(iterable, predicate);
383
+ }
384
+
385
+ /**
386
+ * This function finds the first element matching the predicate.
387
+ */
388
+ export function find<T>(
389
+ iterable: Iterable<T>,
390
+ predicate: (value: T) => boolean
391
+ ): T | undefined {
392
+ for (const value of iterable) {
393
+ if (predicate(value)) return value;
394
+ }
395
+ return undefined;
396
+ }
397
+
398
+ /**
399
+ * This function counts elements (optionally matching a predicate).
400
+ */
401
+ export function count<T>(
402
+ iterable: Iterable<T>,
403
+ predicate?: (value: T) => boolean
404
+ ): number {
405
+ let count = 0;
406
+ for (const value of iterable) {
407
+ if (!predicate || predicate(value)) {
408
+ count++;
409
+ }
410
+ }
411
+ return count;
412
+ }
413
+
414
+ // ============================================================================
415
+ // Min/Max
416
+ // ============================================================================
417
+
418
+ /**
419
+ * This function gives the minimum element.
420
+ */
421
+ export function min<T>(
422
+ iterable: Iterable<T>,
423
+ compare?: (a: T, b: T) => number
424
+ ): T | undefined {
425
+ const cmp = compare || ((a: any, b: any) => a - b);
426
+ let minValue: T | undefined;
427
+
428
+ for (const value of iterable) {
429
+ if (minValue === undefined || cmp(value, minValue) < 0) {
430
+ minValue = value;
431
+ }
432
+ }
433
+
434
+ return minValue;
435
+ }
436
+
437
+ /**
438
+ * This function gives the maximum element.
439
+ */
440
+ export function max<T>(
441
+ iterable: Iterable<T>,
442
+ compare?: (a: T, b: T) => number
443
+ ): T | undefined {
444
+ const cmp = compare || ((a: any, b: any) => a - b);
445
+ let maxValue: T | undefined;
446
+
447
+ for (const value of iterable) {
448
+ if (maxValue === undefined || cmp(value, maxValue) > 0) {
449
+ maxValue = value;
450
+ }
451
+ }
452
+
453
+ return maxValue;
454
+ }
455
+
456
+ /**
457
+ * This function gives the sum of elements.
458
+ */
459
+ export function sum(iterable: Iterable<number>): number {
460
+ let total = 0;
461
+ for (const value of iterable) {
462
+ total += value;
463
+ }
464
+ return total;
465
+ }
466
+
467
+ /**
468
+ * This function gives the average of elements.
469
+ */
470
+ export function average(iterable: Iterable<number>): number | undefined {
471
+ let total = 0;
472
+ let count = 0;
473
+
474
+ for (const value of iterable) {
475
+ total += value;
476
+ count++;
477
+ }
478
+
479
+ return count === 0 ? undefined : total / count;
480
+ }
481
+
482
+ // ============================================================================
483
+ // Terminal operations (materialization)
484
+ // ============================================================================
485
+
486
+ /**
487
+ * This function collects elements into an array.
488
+ */
489
+ export function toArray<T>(iterable: Iterable<T>): T[] {
490
+ return Array.from(iterable);
491
+ }
492
+
493
+ /**
494
+ * This function collects elements into a Set.
495
+ */
496
+ export function toSet<T>(iterable: Iterable<T>): Set<T> {
497
+ return new Set(iterable);
498
+ }
499
+
500
+ /**
501
+ * This function collects elements into a Map.
502
+ */
503
+ export function toMap<T, K, V>(
504
+ iterable: Iterable<T>,
505
+ keyFn: (value: T) => K,
506
+ valueFn: (value: T) => V
507
+ ): Map<K, V> {
508
+ const map = new Map<K, V>();
509
+ for (const value of iterable) {
510
+ map.set(keyFn(value), valueFn(value));
511
+ }
512
+ return map;
513
+ }
514
+
515
+ // ============================================================================
516
+ // Pipe utility for composition
517
+ // ============================================================================
518
+
519
+ /**
520
+ * This function pipes a value through a series of transformations.
521
+ *
522
+ * @example
523
+ * const result = pipe(
524
+ * [1, 2, 3, 4, 5],
525
+ * (it) => map(it, x => x * 2),
526
+ * (it) => filter(it, x => x > 5),
527
+ * (it) => take(it, 2),
528
+ * toArray
529
+ * ) // [6, 8]
530
+ */
531
+ export function pipe<T>(
532
+ value: T,
533
+ ...fns: Array<(value: any) => any>
534
+ ): any {
535
+ return fns.reduce((acc, fn) => fn(acc), value);
536
+ }
@@ -0,0 +1,53 @@
1
+ /** An undirected edge between the nodes `a` and `b`. The default weight is 1. */
2
+ export interface WeightedEdge {
3
+ readonly a: number;
4
+ readonly b: number;
5
+ readonly weight?: number | undefined;
6
+ }
7
+
8
+ /**
9
+ * This function gives a breadth-first order of the nodes `0` to `n - 1` of an undirected weighted graph.
10
+ *
11
+ * - The hub of a component is the node with the largest sum of edge weights. `compare`, then the lower index, breaks a tie.
12
+ * - The order starts at the hub of each component, and goes breadth-first. `compare`, then the lower index, orders the neighbours of a node.
13
+ * - Without edges, each node is a component, so the order is the order of `compare`.
14
+ * - An edge from a node to itself adds no weight.
15
+ * - A bad `n`, an index outside the nodes, or a weight that is not a finite number above 0, throws a `RangeError`.
16
+ *
17
+ * The function is pure. It keeps no state, and it does not change its arguments.
18
+ */
19
+ export function hubBreadthFirstOrder(n: number, edges: readonly WeightedEdge[] = [], compare: (i: number, j: number) => number = () => 0): number[] {
20
+ if (!Number.isInteger(n) || n < 0) throw new RangeError(`The count of the nodes is a whole number from 0, not ${n}.`);
21
+ const degree = new Float64Array(n);
22
+ const next: number[][] = Array.from({ length: n }, () => []);
23
+ for (const { a, b, weight = 1 } of edges) {
24
+ if (!Number.isInteger(a) || !Number.isInteger(b) || a < 0 || b < 0 || a >= n || b >= n) {
25
+ throw new RangeError(`An edge joins the nodes ${a} and ${b}, but the indexes go from 0 to ${n - 1}.`);
26
+ }
27
+ if (!Number.isFinite(weight) || weight <= 0) throw new RangeError(`The weight of an edge is a finite number above 0, not ${weight}.`);
28
+ if (a === b) continue;
29
+ degree[a] = degree[a]! + weight;
30
+ degree[b] = degree[b]! + weight;
31
+ next[a]!.push(b);
32
+ next[b]!.push(a);
33
+ }
34
+ const order = (i: number, j: number): number => compare(i, j) || i - j;
35
+ const hubs = Array.from({ length: n }, (_, i) => i).sort((i, j) => degree[j]! - degree[i]! || order(i, j));
36
+ const seen = new Uint8Array(n);
37
+ const out: number[] = [];
38
+ for (const hub of hubs) {
39
+ if (seen[hub] === 1) continue;
40
+ seen[hub] = 1;
41
+ const queue = [hub];
42
+ for (let k = 0; k < queue.length; k++) {
43
+ const i = queue[k]!;
44
+ out.push(i);
45
+ for (const j of next[i]!.toSorted(order)) {
46
+ if (seen[j] === 1) continue;
47
+ seen[j] = 1;
48
+ queue.push(j);
49
+ }
50
+ }
51
+ }
52
+ return out;
53
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The entry `@mark1russell7/collections/graph` holds pure graph functions. It imports no other package.
3
+ */
4
+
5
+ export { hubBreadthFirstOrder, type WeightedEdge } from "./hub-order.js";