@mark1russell7/collections 0.0.0-bootstrap.0 → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +77 -1
- package/build/dag/index.d.ts +144 -0
- package/build/dag/index.d.ts.map +1 -0
- package/build/dag/index.js +294 -0
- package/build/dag/index.js.map +1 -0
- package/build/graph/index.d.ts +20 -0
- package/build/graph/index.d.ts.map +1 -0
- package/build/graph/index.js +48 -0
- package/build/graph/index.js.map +1 -0
- package/build/index.d.ts +4952 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +7811 -0
- package/build/index.js.map +1 -0
- package/build/testing/index.d.ts +25 -0
- package/build/testing/index.d.ts.map +1 -0
- package/build/testing/index.js +72 -0
- package/build/testing/index.js.map +1 -0
- package/package.json +71 -4
- package/src/async/async-queue.ts +481 -0
- package/src/async/channels.ts +450 -0
- package/src/async/settled.ts +41 -0
- package/src/behaviors/bounded.ts +485 -0
- package/src/behaviors/composite.ts +99 -0
- package/src/behaviors/evented.ts +464 -0
- package/src/behaviors/lru.ts +449 -0
- package/src/behaviors/readonly.ts +236 -0
- package/src/behaviors/safe.ts +358 -0
- package/src/behaviors/synchronized.ts +316 -0
- package/src/behaviors/ttl.ts +464 -0
- package/src/core/effects.ts +327 -0
- package/src/core/events.ts +363 -0
- package/src/core/middleware.ts +413 -0
- package/src/core/policies.ts +316 -0
- package/src/core/traits.ts +164 -0
- package/src/dag/builder.ts +124 -0
- package/src/dag/executor.ts +190 -0
- package/src/dag/index.ts +14 -0
- package/src/dag/traversal.ts +129 -0
- package/src/dag/types.ts +87 -0
- package/src/fx/collectors.ts +500 -0
- package/src/fx/iter.ts +536 -0
- package/src/graph/hub-order.ts +53 -0
- package/src/graph/index.ts +5 -0
- package/src/impl/array-deque.ts +463 -0
- package/src/impl/array-list.ts +490 -0
- package/src/impl/hash-map.ts +623 -0
- package/src/impl/hash-set.ts +342 -0
- package/src/impl/linked-hash-map.ts +572 -0
- package/src/impl/linked-list.ts +610 -0
- package/src/impl/priority-queue.ts +448 -0
- package/src/impl/summary-tree.ts +594 -0
- package/src/impl/tree-map.ts +913 -0
- package/src/impl/tree-set.ts +469 -0
- package/src/index.ts +237 -0
- package/src/interfaces/collection.ts +141 -0
- package/src/interfaces/list.ts +281 -0
- package/src/interfaces/map.ts +405 -0
- package/src/interfaces/queue.ts +353 -0
- package/src/interfaces/set.ts +158 -0
- package/src/order/index.ts +6 -0
- package/src/order/rank.ts +271 -0
- package/src/storage/listeners.ts +72 -0
- package/src/testing/check.ts +81 -0
- package/src/testing/index.ts +7 -0
- package/src/utils/defaults.ts +345 -0
- package/src/utils/factories.ts +357 -0
- 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
|
+
}
|