@mark1russell7/collections 0.9.0 → 0.9.2
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/README.md +2 -0
- package/build/index.d.ts +39 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +74 -31
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/async/channels.ts +26 -0
- package/src/behaviors/readonly.ts +6 -0
- package/src/impl/hash-set.ts +30 -22
- package/src/impl/tree-set.ts +18 -18
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/collections",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.2",
|
|
5
5
|
"description": "Collections with no dependencies: lists, maps, queues, a summary tree, channels, lazy operations and dependency graphs.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"collections",
|
package/src/async/channels.ts
CHANGED
|
@@ -27,6 +27,8 @@ import { asyncQueue, type AsyncQueue } from "./async-queue.js";
|
|
|
27
27
|
*
|
|
28
28
|
* The channels of Go are the model. This class gives send/receive operations
|
|
29
29
|
* that can block until the other side is ready.
|
|
30
|
+
*
|
|
31
|
+
* @deprecated Import `Channel` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
30
32
|
*/
|
|
31
33
|
export class Channel<T> {
|
|
32
34
|
private queue: AsyncQueue<T>;
|
|
@@ -140,6 +142,8 @@ export class Channel<T> {
|
|
|
140
142
|
|
|
141
143
|
/**
|
|
142
144
|
* SelectCase represents a case in a select statement.
|
|
145
|
+
*
|
|
146
|
+
* @deprecated Import `SelectCase` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
143
147
|
*/
|
|
144
148
|
export interface SelectCase<R> {
|
|
145
149
|
channel: Channel<any>;
|
|
@@ -212,6 +216,8 @@ function firstOf(waits: readonly Wait[]): Promise<{ index: number; value: unknow
|
|
|
212
216
|
* ch2.case((v) => `ch2: ${v}`),
|
|
213
217
|
* timeout(1000).case(() => 'timeout')
|
|
214
218
|
* )
|
|
219
|
+
*
|
|
220
|
+
* @deprecated Import `select` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
215
221
|
*/
|
|
216
222
|
export async function select<R>(...cases: SelectCase<R>[]): Promise<R> {
|
|
217
223
|
const { index, value } = await firstOf(
|
|
@@ -229,6 +235,8 @@ export async function select<R>(...cases: SelectCase<R>[]): Promise<R> {
|
|
|
229
235
|
* dataChannel.case((v) => v),
|
|
230
236
|
* timeout(5000).case(() => 'timeout!')
|
|
231
237
|
* )
|
|
238
|
+
*
|
|
239
|
+
* @deprecated Import `timeout` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
232
240
|
*/
|
|
233
241
|
export function timeout(ms: number): Channel<void> {
|
|
234
242
|
const ch = new Channel<void>();
|
|
@@ -247,6 +255,8 @@ export function timeout(ms: number): Channel<void> {
|
|
|
247
255
|
* for await (const t of tick) {
|
|
248
256
|
* console.log('Tick:', t)
|
|
249
257
|
* }
|
|
258
|
+
*
|
|
259
|
+
* @deprecated Import `ticker` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
250
260
|
*/
|
|
251
261
|
export function ticker(ms: number): Channel<number> & { stop: () => void } {
|
|
252
262
|
const ch = new Channel<number>(1);
|
|
@@ -273,6 +283,8 @@ export function ticker(ms: number): Channel<number> & { stop: () => void } {
|
|
|
273
283
|
*
|
|
274
284
|
* await input.send(5)
|
|
275
285
|
* const result = await output.receive() // 10
|
|
286
|
+
*
|
|
287
|
+
* @deprecated Import `pipeline` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
276
288
|
*/
|
|
277
289
|
export function pipeline<T, U>(
|
|
278
290
|
input: Channel<T>,
|
|
@@ -305,6 +317,8 @@ export function pipeline<T, U>(
|
|
|
305
317
|
* const [out1, out2, out3] = fanOut(input, 3)
|
|
306
318
|
*
|
|
307
319
|
* // Each output gets a copy of every input value
|
|
320
|
+
*
|
|
321
|
+
* @deprecated Import `fanOut` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
308
322
|
*/
|
|
309
323
|
export function fanOut<T>(input: Channel<T>, count: number, bufferSize: number = 0): Channel<T>[] {
|
|
310
324
|
const outputs = Array.from({ length: count }, () => new Channel<T>(bufferSize));
|
|
@@ -331,6 +345,8 @@ export function fanOut<T>(input: Channel<T>, count: number, bufferSize: number =
|
|
|
331
345
|
* const merged = fanIn(ch1, ch2)
|
|
332
346
|
*
|
|
333
347
|
* // merged receives values from both ch1 and ch2
|
|
348
|
+
*
|
|
349
|
+
* @deprecated Import `fanIn` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
334
350
|
*/
|
|
335
351
|
export function fanIn<T>(...inputs: Channel<T>[]): Channel<T> {
|
|
336
352
|
const output = new Channel<T>();
|
|
@@ -356,6 +372,8 @@ export function fanIn<T>(...inputs: Channel<T>[]): Channel<T> {
|
|
|
356
372
|
* It takes one value at a time, from the inputs in turn. It keeps no receive on an input while
|
|
357
373
|
* it waits for the reader of the output. Thus each value of each input reaches the output one
|
|
358
374
|
* time, in the order of its input, or stays in its input.
|
|
375
|
+
*
|
|
376
|
+
* @deprecated Import `merge` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
359
377
|
*/
|
|
360
378
|
export function merge<T>(...inputs: Channel<T>[]): Channel<T> {
|
|
361
379
|
const output = new Channel<T>();
|
|
@@ -389,6 +407,8 @@ export function merge<T>(...inputs: Channel<T>[]): Channel<T> {
|
|
|
389
407
|
|
|
390
408
|
/**
|
|
391
409
|
* Buffered channel with specified capacity.
|
|
410
|
+
*
|
|
411
|
+
* @deprecated Import `buffered` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
392
412
|
*/
|
|
393
413
|
export function buffered<T>(capacity: number): Channel<T> {
|
|
394
414
|
return new Channel<T>(capacity);
|
|
@@ -397,6 +417,8 @@ export function buffered<T>(capacity: number): Channel<T> {
|
|
|
397
417
|
/**
|
|
398
418
|
* Unbuffered channel (rendezvous point).
|
|
399
419
|
* Sends block until receiver is ready.
|
|
420
|
+
*
|
|
421
|
+
* @deprecated Import `unbuffered` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
400
422
|
*/
|
|
401
423
|
export function unbuffered<T>(): Channel<T> {
|
|
402
424
|
return new Channel<T>(0);
|
|
@@ -408,6 +430,8 @@ export function unbuffered<T>(): Channel<T> {
|
|
|
408
430
|
* @example
|
|
409
431
|
* const ch = channel<number>()
|
|
410
432
|
* const bufferedCh = channel<string>(10)
|
|
433
|
+
*
|
|
434
|
+
* @deprecated Import `channel` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
411
435
|
*/
|
|
412
436
|
export function channel<T>(bufferSize: number = 0): Channel<T> {
|
|
413
437
|
return new Channel<T>(bufferSize);
|
|
@@ -424,6 +448,8 @@ export function channel<T>(bufferSize: number = 0): Channel<T> {
|
|
|
424
448
|
* )
|
|
425
449
|
*
|
|
426
450
|
* // 4 workers concurrently process jobs from jobChannel
|
|
451
|
+
*
|
|
452
|
+
* @deprecated Import `workerPool` from `@mark1russell7/service/channel`. The next MINOR version of collections removes it.
|
|
427
453
|
*/
|
|
428
454
|
export function workerPool<T, R>(
|
|
429
455
|
jobs: Channel<T>,
|
|
@@ -88,6 +88,9 @@ const MUTATING_METHODS = new Set([
|
|
|
88
88
|
// Queue changes that give undefined on an empty queue (C27: a readonly view removed an element)
|
|
89
89
|
"pollOrUndefined",
|
|
90
90
|
|
|
91
|
+
// The removal by a test of LRUCache (C31: a readonly view of an LRU cache emptied it)
|
|
92
|
+
"deleteWhere",
|
|
93
|
+
|
|
91
94
|
// The internal changes of the structures of this package. TypeScript marks them private, but a call at run
|
|
92
95
|
// time reaches them. `readonly.test.ts` puts each method of each structure in the reads or in this list.
|
|
93
96
|
"ensureCapacity",
|
|
@@ -108,6 +111,9 @@ const MUTATING_METHODS = new Set([
|
|
|
108
111
|
"fixAfterDeletion",
|
|
109
112
|
"rotateLeft",
|
|
110
113
|
"rotateRight",
|
|
114
|
+
"moveToHead",
|
|
115
|
+
"removeNode",
|
|
116
|
+
"evictLRU",
|
|
111
117
|
]);
|
|
112
118
|
|
|
113
119
|
/** Methods that return a view of the collection: the view is readonly too. */
|
package/src/impl/hash-set.ts
CHANGED
|
@@ -63,6 +63,7 @@ export interface HashSetOptions<T> {
|
|
|
63
63
|
export class HashSet<T> implements Set<T> {
|
|
64
64
|
private readonly _map: HashMap<T, symbol>;
|
|
65
65
|
private readonly _eq: Eq<T>;
|
|
66
|
+
private readonly _hash: Hash<T>;
|
|
66
67
|
|
|
67
68
|
constructor(options: HashSetOptions<T> = {}) {
|
|
68
69
|
const {
|
|
@@ -73,6 +74,7 @@ export class HashSet<T> implements Set<T> {
|
|
|
73
74
|
} = options;
|
|
74
75
|
|
|
75
76
|
this._eq = eq;
|
|
77
|
+
this._hash = hash;
|
|
76
78
|
this._map = new HashMap<T, symbol>({
|
|
77
79
|
initialCapacity,
|
|
78
80
|
loadFactor,
|
|
@@ -140,18 +142,11 @@ export class HashSet<T> implements Set<T> {
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
retainAll(other: Iterable<T>): boolean {
|
|
143
|
-
const toRetain =
|
|
145
|
+
const toRetain = this.setOf(other);
|
|
144
146
|
const toRemove: T[] = [];
|
|
145
147
|
|
|
146
148
|
for (const element of this) {
|
|
147
|
-
|
|
148
|
-
for (const retainElement of toRetain) {
|
|
149
|
-
if (this._eq(element, retainElement)) {
|
|
150
|
-
shouldRetain = true;
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (!shouldRetain) {
|
|
149
|
+
if (!toRetain.contains(element)) {
|
|
155
150
|
toRemove.push(element);
|
|
156
151
|
}
|
|
157
152
|
}
|
|
@@ -197,13 +192,12 @@ export class HashSet<T> implements Set<T> {
|
|
|
197
192
|
* The new set contains only elements present in both sets.
|
|
198
193
|
*/
|
|
199
194
|
intersection(other: Iterable<T>): HashSet<T> {
|
|
200
|
-
|
|
195
|
+
// The loop reads `other` one time, so a generator gives all of its elements
|
|
196
|
+
const others = this.setOf(other);
|
|
197
|
+
const result = this.empty();
|
|
201
198
|
for (const element of this) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
result.add(element);
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
199
|
+
if (others.contains(element)) {
|
|
200
|
+
result.add(element);
|
|
207
201
|
}
|
|
208
202
|
}
|
|
209
203
|
return result;
|
|
@@ -224,8 +218,10 @@ export class HashSet<T> implements Set<T> {
|
|
|
224
218
|
* The new set contains elements in either set but not in both.
|
|
225
219
|
*/
|
|
226
220
|
symmetricDifference(other: Iterable<T>): HashSet<T> {
|
|
227
|
-
|
|
228
|
-
const
|
|
221
|
+
// The union and the intersection each read `other`, so read it one time first
|
|
222
|
+
const others = this.setOf(other);
|
|
223
|
+
const result = this.union(others);
|
|
224
|
+
const inter = this.intersection(others);
|
|
229
225
|
result.removeAll(inter);
|
|
230
226
|
return result;
|
|
231
227
|
}
|
|
@@ -235,8 +231,7 @@ export class HashSet<T> implements Set<T> {
|
|
|
235
231
|
* All elements in this set are also in the other set.
|
|
236
232
|
*/
|
|
237
233
|
isSubsetOf(other: Iterable<T>): boolean {
|
|
238
|
-
const otherSet =
|
|
239
|
-
otherSet.addAll(other);
|
|
234
|
+
const otherSet = this.setOf(other);
|
|
240
235
|
|
|
241
236
|
for (const element of this) {
|
|
242
237
|
if (!otherSet.contains(element)) {
|
|
@@ -300,9 +295,22 @@ export class HashSet<T> implements Set<T> {
|
|
|
300
295
|
* This method makes a shallow copy of this set.
|
|
301
296
|
*/
|
|
302
297
|
clone(): HashSet<T> {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
298
|
+
return this.setOf(this);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* This method makes an empty set with the equality and the hash function of this set.
|
|
303
|
+
* Before, the inner sets kept only the equality, so they did not find an element with a custom hash.
|
|
304
|
+
*/
|
|
305
|
+
private empty(): HashSet<T> {
|
|
306
|
+
return new HashSet<T>({ eq: this._eq, hash: this._hash });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** This method makes a set of the elements, with the equality and the hash function of this set. */
|
|
310
|
+
private setOf(elements: Iterable<T>): HashSet<T> {
|
|
311
|
+
const set = this.empty();
|
|
312
|
+
set.addAll(elements);
|
|
313
|
+
return set;
|
|
306
314
|
}
|
|
307
315
|
|
|
308
316
|
/**
|
package/src/impl/tree-set.ts
CHANGED
|
@@ -160,10 +160,7 @@ export class TreeSet<T> implements NavigableSet<T> {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
retainAll(elements: Iterable<T>): boolean {
|
|
163
|
-
const toRetain =
|
|
164
|
-
for (const element of elements) {
|
|
165
|
-
toRetain.add(element);
|
|
166
|
-
}
|
|
163
|
+
const toRetain = this.setOf(elements);
|
|
167
164
|
|
|
168
165
|
let modified = false;
|
|
169
166
|
for (const element of Array.from(this)) {
|
|
@@ -201,10 +198,7 @@ export class TreeSet<T> implements NavigableSet<T> {
|
|
|
201
198
|
eq: this._eq,
|
|
202
199
|
});
|
|
203
200
|
|
|
204
|
-
const otherSet =
|
|
205
|
-
for (const element of other) {
|
|
206
|
-
otherSet.add(element);
|
|
207
|
-
}
|
|
201
|
+
const otherSet = this.setOf(other);
|
|
208
202
|
|
|
209
203
|
for (const element of this) {
|
|
210
204
|
if (otherSet.has(element)) {
|
|
@@ -221,10 +215,7 @@ export class TreeSet<T> implements NavigableSet<T> {
|
|
|
221
215
|
eq: this._eq,
|
|
222
216
|
});
|
|
223
217
|
|
|
224
|
-
const otherSet =
|
|
225
|
-
for (const element of other) {
|
|
226
|
-
otherSet.add(element);
|
|
227
|
-
}
|
|
218
|
+
const otherSet = this.setOf(other);
|
|
228
219
|
|
|
229
220
|
for (const element of this) {
|
|
230
221
|
if (!otherSet.has(element)) {
|
|
@@ -246,8 +237,8 @@ export class TreeSet<T> implements NavigableSet<T> {
|
|
|
246
237
|
result.add(element);
|
|
247
238
|
}
|
|
248
239
|
|
|
249
|
-
// Toggle
|
|
250
|
-
for (const element of other) {
|
|
240
|
+
// Toggle each element of the other set one time, so a repeated element does not toggle back
|
|
241
|
+
for (const element of this.setOf(other)) {
|
|
251
242
|
if (result.has(element)) {
|
|
252
243
|
result.remove(element);
|
|
253
244
|
} else {
|
|
@@ -259,10 +250,7 @@ export class TreeSet<T> implements NavigableSet<T> {
|
|
|
259
250
|
}
|
|
260
251
|
|
|
261
252
|
isSubsetOf(other: Iterable<T>): boolean {
|
|
262
|
-
const otherSet =
|
|
263
|
-
for (const element of other) {
|
|
264
|
-
otherSet.add(element);
|
|
265
|
-
}
|
|
253
|
+
const otherSet = this.setOf(other);
|
|
266
254
|
|
|
267
255
|
for (const element of this) {
|
|
268
256
|
if (!otherSet.has(element)) {
|
|
@@ -272,6 +260,18 @@ export class TreeSet<T> implements NavigableSet<T> {
|
|
|
272
260
|
return true;
|
|
273
261
|
}
|
|
274
262
|
|
|
263
|
+
/**
|
|
264
|
+
* This method makes a set of the elements, with the comparator and the equality of this set.
|
|
265
|
+
* Before, the set operations used a native `Set`, which compares with `===` and ignores the comparator.
|
|
266
|
+
*/
|
|
267
|
+
private setOf(elements: Iterable<T>): TreeSet<T> {
|
|
268
|
+
const set = new TreeSet<T>({ compare: this._compare, eq: this._eq });
|
|
269
|
+
for (const element of elements) {
|
|
270
|
+
set.add(element);
|
|
271
|
+
}
|
|
272
|
+
return set;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
275
|
isSupersetOf(other: Iterable<T>): boolean {
|
|
276
276
|
for (const element of other) {
|
|
277
277
|
if (!this.has(element)) {
|