@nlozgachev/pipelined 0.10.0 → 0.12.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/README.md +9 -3
- package/esm/src/Core/Option.js +2 -1
- package/esm/src/Core/RemoteData.js +5 -5
- package/esm/src/Core/TaskValidation.js +1 -3
- package/esm/src/Core/Tuple.js +112 -0
- package/esm/src/Core/index.js +4 -5
- package/esm/src/{Core → Utils}/Arr.js +95 -23
- package/esm/src/Utils/Dict.js +421 -0
- package/esm/src/Utils/Num.js +124 -0
- package/esm/src/{Core → Utils}/Rec.js +85 -11
- package/esm/src/Utils/Str.js +134 -0
- package/esm/src/Utils/Uniq.js +265 -0
- package/esm/src/Utils/index.js +6 -0
- package/package.json +11 -1
- package/script/src/Core/Option.js +2 -1
- package/script/src/Core/RemoteData.js +5 -5
- package/script/src/Core/TaskValidation.js +1 -3
- package/script/src/Core/Tuple.js +115 -0
- package/script/src/Core/index.js +4 -5
- package/script/src/{Core → Utils}/Arr.js +95 -23
- package/script/src/Utils/Dict.js +424 -0
- package/script/src/Utils/Num.js +127 -0
- package/script/src/{Core → Utils}/Rec.js +85 -11
- package/script/src/Utils/Str.js +137 -0
- package/script/src/Utils/Uniq.js +268 -0
- package/script/src/Utils/index.js +22 -0
- package/types/src/Composition/compose.d.ts.map +1 -1
- package/types/src/Composition/converge.d.ts.map +1 -1
- package/types/src/Composition/curry.d.ts.map +1 -1
- package/types/src/Composition/flow.d.ts.map +1 -1
- package/types/src/Composition/fn.d.ts.map +1 -1
- package/types/src/Composition/juxt.d.ts.map +1 -1
- package/types/src/Composition/memoize.d.ts.map +1 -1
- package/types/src/Composition/not.d.ts.map +1 -1
- package/types/src/Composition/on.d.ts.map +1 -1
- package/types/src/Composition/pipe.d.ts.map +1 -1
- package/types/src/Composition/uncurry.d.ts.map +1 -1
- package/types/src/Core/Deferred.d.ts.map +1 -1
- package/types/src/Core/Lens.d.ts.map +1 -1
- package/types/src/Core/Logged.d.ts.map +1 -1
- package/types/src/Core/Option.d.ts.map +1 -1
- package/types/src/Core/Optional.d.ts.map +1 -1
- package/types/src/Core/Predicate.d.ts.map +1 -1
- package/types/src/Core/Reader.d.ts.map +1 -1
- package/types/src/Core/Refinement.d.ts.map +1 -1
- package/types/src/Core/RemoteData.d.ts.map +1 -1
- package/types/src/Core/Result.d.ts.map +1 -1
- package/types/src/Core/State.d.ts.map +1 -1
- package/types/src/Core/Task.d.ts.map +1 -1
- package/types/src/Core/TaskOption.d.ts.map +1 -1
- package/types/src/Core/TaskResult.d.ts.map +1 -1
- package/types/src/Core/TaskValidation.d.ts.map +1 -1
- package/types/src/Core/These.d.ts.map +1 -1
- package/types/src/Core/Tuple.d.ts +129 -0
- package/types/src/Core/Tuple.d.ts.map +1 -0
- package/types/src/Core/Validation.d.ts.map +1 -1
- package/types/src/Core/index.d.ts +4 -5
- package/types/src/Core/index.d.ts.map +1 -1
- package/types/src/Types/Brand.d.ts.map +1 -1
- package/types/src/Types/NonEmptyList.d.ts.map +1 -1
- package/types/src/{Core → Utils}/Arr.d.ts +25 -3
- package/types/src/Utils/Arr.d.ts.map +1 -0
- package/types/src/Utils/Dict.d.ts +310 -0
- package/types/src/Utils/Dict.d.ts.map +1 -0
- package/types/src/Utils/Num.d.ts +110 -0
- package/types/src/Utils/Num.d.ts.map +1 -0
- package/types/src/{Core → Utils}/Rec.d.ts +39 -1
- package/types/src/Utils/Rec.d.ts.map +1 -0
- package/types/src/Utils/Str.d.ts +128 -0
- package/types/src/Utils/Str.d.ts.map +1 -0
- package/types/src/Utils/Uniq.d.ts +179 -0
- package/types/src/Utils/Uniq.d.ts.map +1 -0
- package/types/src/Utils/index.d.ts +7 -0
- package/types/src/Utils/index.d.ts.map +1 -0
- package/types/src/Core/Arr.d.ts.map +0 -1
- package/types/src/Core/Rec.d.ts.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Arr = void 0;
|
|
4
|
-
const Deferred_js_1 = require("
|
|
5
|
-
const Option_js_1 = require("
|
|
6
|
-
const Result_js_1 = require("
|
|
7
|
-
const Task_js_1 = require("
|
|
4
|
+
const Deferred_js_1 = require("../Core/Deferred.js");
|
|
5
|
+
const Option_js_1 = require("../Core/Option.js");
|
|
6
|
+
const Result_js_1 = require("../Core/Result.js");
|
|
7
|
+
const Task_js_1 = require("../Core/Task.js");
|
|
8
8
|
const NonEmptyList_js_1 = require("../Types/NonEmptyList.js");
|
|
9
9
|
/**
|
|
10
10
|
* Functional array utilities that compose well with pipe.
|
|
@@ -113,7 +113,13 @@ var Arr;
|
|
|
113
113
|
* pipe([1, 2, 3], Arr.map(n => n * 2)); // [2, 4, 6]
|
|
114
114
|
* ```
|
|
115
115
|
*/
|
|
116
|
-
Arr.map = (f) => (data) =>
|
|
116
|
+
Arr.map = (f) => (data) => {
|
|
117
|
+
const n = data.length;
|
|
118
|
+
const result = new Array(n);
|
|
119
|
+
for (let i = 0; i < n; i++)
|
|
120
|
+
result[i] = f(data[i]);
|
|
121
|
+
return result;
|
|
122
|
+
};
|
|
117
123
|
/**
|
|
118
124
|
* Filters elements that satisfy the predicate.
|
|
119
125
|
*
|
|
@@ -122,7 +128,15 @@ var Arr;
|
|
|
122
128
|
* pipe([1, 2, 3, 4], Arr.filter(n => n % 2 === 0)); // [2, 4]
|
|
123
129
|
* ```
|
|
124
130
|
*/
|
|
125
|
-
Arr.filter = (predicate) => (data) =>
|
|
131
|
+
Arr.filter = (predicate) => (data) => {
|
|
132
|
+
const n = data.length;
|
|
133
|
+
const result = [];
|
|
134
|
+
for (let i = 0; i < n; i++) {
|
|
135
|
+
if (predicate(data[i]))
|
|
136
|
+
result.push(data[i]);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
};
|
|
126
140
|
/**
|
|
127
141
|
* Splits an array into two groups based on a predicate.
|
|
128
142
|
* First group contains elements that satisfy the predicate,
|
|
@@ -216,9 +230,9 @@ var Arr;
|
|
|
216
230
|
*/
|
|
217
231
|
Arr.zip = (other) => (data) => {
|
|
218
232
|
const len = Math.min(data.length, other.length);
|
|
219
|
-
const result =
|
|
233
|
+
const result = new Array(len);
|
|
220
234
|
for (let i = 0; i < len; i++) {
|
|
221
|
-
result
|
|
235
|
+
result[i] = [data[i], other[i]];
|
|
222
236
|
}
|
|
223
237
|
return result;
|
|
224
238
|
};
|
|
@@ -232,9 +246,9 @@ var Arr;
|
|
|
232
246
|
*/
|
|
233
247
|
Arr.zipWith = (f) => (other) => (data) => {
|
|
234
248
|
const len = Math.min(data.length, other.length);
|
|
235
|
-
const result =
|
|
249
|
+
const result = new Array(len);
|
|
236
250
|
for (let i = 0; i < len; i++) {
|
|
237
|
-
result
|
|
251
|
+
result[i] = f(data[i], other[i]);
|
|
238
252
|
}
|
|
239
253
|
return result;
|
|
240
254
|
};
|
|
@@ -289,7 +303,17 @@ var Arr;
|
|
|
289
303
|
* pipe([1, 2, 3], Arr.flatMap(n => [n, n * 10])); // [1, 10, 2, 20, 3, 30]
|
|
290
304
|
* ```
|
|
291
305
|
*/
|
|
292
|
-
Arr.flatMap = (f) => (data) =>
|
|
306
|
+
Arr.flatMap = (f) => (data) => {
|
|
307
|
+
const n = data.length;
|
|
308
|
+
const result = [];
|
|
309
|
+
for (let i = 0; i < n; i++) {
|
|
310
|
+
const chunk = f(data[i]);
|
|
311
|
+
const m = chunk.length;
|
|
312
|
+
for (let j = 0; j < m; j++)
|
|
313
|
+
result.push(chunk[j]);
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
316
|
+
};
|
|
293
317
|
/**
|
|
294
318
|
* Reduces an array from the left.
|
|
295
319
|
*
|
|
@@ -316,12 +340,13 @@ var Arr;
|
|
|
316
340
|
* ```
|
|
317
341
|
*/
|
|
318
342
|
Arr.traverse = (f) => (data) => {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
343
|
+
const n = data.length;
|
|
344
|
+
const result = new Array(n);
|
|
345
|
+
for (let i = 0; i < n; i++) {
|
|
346
|
+
const mapped = f(data[i]);
|
|
347
|
+
if (mapped.kind === "None")
|
|
323
348
|
return Option_js_1.Option.none();
|
|
324
|
-
result
|
|
349
|
+
result[i] = mapped.value;
|
|
325
350
|
}
|
|
326
351
|
return Option_js_1.Option.some(result);
|
|
327
352
|
};
|
|
@@ -338,12 +363,13 @@ var Arr;
|
|
|
338
363
|
* ```
|
|
339
364
|
*/
|
|
340
365
|
Arr.traverseResult = (f) => (data) => {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
366
|
+
const n = data.length;
|
|
367
|
+
const result = new Array(n);
|
|
368
|
+
for (let i = 0; i < n; i++) {
|
|
369
|
+
const mapped = f(data[i]);
|
|
370
|
+
if (mapped.kind === "Error")
|
|
345
371
|
return mapped;
|
|
346
|
-
result
|
|
372
|
+
result[i] = mapped.value;
|
|
347
373
|
}
|
|
348
374
|
return Result_js_1.Result.ok(result);
|
|
349
375
|
};
|
|
@@ -430,7 +456,13 @@ var Arr;
|
|
|
430
456
|
* pipe([1, 2, 3], Arr.some(n => n > 2)); // true
|
|
431
457
|
* ```
|
|
432
458
|
*/
|
|
433
|
-
Arr.some = (predicate) => (data) =>
|
|
459
|
+
Arr.some = (predicate) => (data) => {
|
|
460
|
+
const n = data.length;
|
|
461
|
+
for (let i = 0; i < n; i++)
|
|
462
|
+
if (predicate(data[i]))
|
|
463
|
+
return true;
|
|
464
|
+
return false;
|
|
465
|
+
};
|
|
434
466
|
/**
|
|
435
467
|
* Returns true if all elements satisfy the predicate.
|
|
436
468
|
*
|
|
@@ -439,7 +471,13 @@ var Arr;
|
|
|
439
471
|
* pipe([1, 2, 3], Arr.every(n => n > 0)); // true
|
|
440
472
|
* ```
|
|
441
473
|
*/
|
|
442
|
-
Arr.every = (predicate) => (data) =>
|
|
474
|
+
Arr.every = (predicate) => (data) => {
|
|
475
|
+
const n = data.length;
|
|
476
|
+
for (let i = 0; i < n; i++)
|
|
477
|
+
if (!predicate(data[i]))
|
|
478
|
+
return false;
|
|
479
|
+
return true;
|
|
480
|
+
};
|
|
443
481
|
/**
|
|
444
482
|
* Reverses an array. Returns a new array.
|
|
445
483
|
*
|
|
@@ -498,4 +536,38 @@ var Arr;
|
|
|
498
536
|
i++;
|
|
499
537
|
return data.slice(i);
|
|
500
538
|
};
|
|
539
|
+
/**
|
|
540
|
+
* Like `reduce`, but returns every intermediate accumulator as an array.
|
|
541
|
+
* The initial value is not included — the output has the same length as the input.
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```ts
|
|
545
|
+
* pipe([1, 2, 3], Arr.scan(0, (acc, n) => acc + n)); // [1, 3, 6]
|
|
546
|
+
* ```
|
|
547
|
+
*/
|
|
548
|
+
Arr.scan = (initial, f) => (data) => {
|
|
549
|
+
const n = data.length;
|
|
550
|
+
const result = new Array(n);
|
|
551
|
+
let acc = initial;
|
|
552
|
+
for (let i = 0; i < n; i++) {
|
|
553
|
+
acc = f(acc, data[i]);
|
|
554
|
+
result[i] = acc;
|
|
555
|
+
}
|
|
556
|
+
return result;
|
|
557
|
+
};
|
|
558
|
+
/**
|
|
559
|
+
* Splits an array at an index into a `[before, after]` tuple.
|
|
560
|
+
* Negative indices clamp to 0; indices beyond the array length clamp to the end.
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* pipe([1, 2, 3, 4], Arr.splitAt(2)); // [[1, 2], [3, 4]]
|
|
565
|
+
* pipe([1, 2, 3], Arr.splitAt(0)); // [[], [1, 2, 3]]
|
|
566
|
+
* pipe([1, 2, 3], Arr.splitAt(10)); // [[1, 2, 3], []]
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
Arr.splitAt = (index) => (data) => {
|
|
570
|
+
const i = Math.max(0, index);
|
|
571
|
+
return [data.slice(0, i), data.slice(i)];
|
|
572
|
+
};
|
|
501
573
|
})(Arr || (exports.Arr = Arr = {}));
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Dict = void 0;
|
|
4
|
+
const Option_js_1 = require("../Core/Option.js");
|
|
5
|
+
/**
|
|
6
|
+
* Functional utilities for key-value dictionaries (`ReadonlyMap<K, V>`). All functions are pure
|
|
7
|
+
* and data-last — they compose naturally with `pipe`.
|
|
8
|
+
*
|
|
9
|
+
* Unlike plain objects (`Rec`), dictionaries support any key type, preserve insertion order, and
|
|
10
|
+
* make membership checks explicit via `lookup` returning `Option`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { Dict } from "@nlozgachev/pipelined/utils";
|
|
15
|
+
* import { pipe } from "@nlozgachev/pipelined/composition";
|
|
16
|
+
*
|
|
17
|
+
* const scores = pipe(
|
|
18
|
+
* Dict.fromEntries([["alice", 10], ["bob", 8], ["carol", 10]] as const),
|
|
19
|
+
* Dict.filter(n => n >= 10),
|
|
20
|
+
* Dict.map(n => `${n} points`),
|
|
21
|
+
* );
|
|
22
|
+
* // ReadonlyMap { "alice" => "10 points", "carol" => "10 points" }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
var Dict;
|
|
26
|
+
(function (Dict) {
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Constructors
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/**
|
|
31
|
+
* Creates an empty dictionary.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* Dict.empty<string, number>(); // ReadonlyMap {}
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
Dict.empty = () => new globalThis.Map();
|
|
39
|
+
/**
|
|
40
|
+
* Creates a dictionary with a single entry.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* Dict.singleton("name", "Alice"); // ReadonlyMap { "name" => "Alice" }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
Dict.singleton = (key, value) => new globalThis.Map([[key, value]]);
|
|
48
|
+
/**
|
|
49
|
+
* Creates a dictionary from an array of key-value pairs.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* Dict.fromEntries([["a", 1], ["b", 2]]); // ReadonlyMap { "a" => 1, "b" => 2 }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
Dict.fromEntries = (entries) => new globalThis.Map(entries);
|
|
57
|
+
/**
|
|
58
|
+
* Creates a dictionary from a plain object. Keys are always strings.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* Dict.fromRecord({ a: 1, b: 2 }); // ReadonlyMap { "a" => 1, "b" => 2 }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
Dict.fromRecord = (rec) => new globalThis.Map(Object.entries(rec));
|
|
66
|
+
/**
|
|
67
|
+
* Groups elements of an array into a dictionary keyed by the result of `keyFn`. Each key maps
|
|
68
|
+
* to the array of elements that produced it, in insertion order. Uses the native `Map.groupBy`
|
|
69
|
+
* when available, falling back to a manual loop in older environments.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* pipe(
|
|
74
|
+
* [{ name: "alice", role: "admin" }, { name: "bob", role: "viewer" }, { name: "carol", role: "admin" }],
|
|
75
|
+
* Dict.groupBy(user => user.role),
|
|
76
|
+
* );
|
|
77
|
+
* // ReadonlyMap { "admin" => [alice, carol], "viewer" => [bob] }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
Dict.groupBy = (keyFn) => (items) => {
|
|
81
|
+
const result = new globalThis.Map();
|
|
82
|
+
for (const item of items) {
|
|
83
|
+
const key = keyFn(item);
|
|
84
|
+
const arr = result.get(key);
|
|
85
|
+
if (arr !== undefined)
|
|
86
|
+
arr.push(item);
|
|
87
|
+
else
|
|
88
|
+
result.set(key, [item]);
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
};
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Query
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
/**
|
|
96
|
+
* Returns `true` if the dictionary contains the given key.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* pipe(Dict.fromEntries([["a", 1]]), Dict.has("a")); // true
|
|
101
|
+
* pipe(Dict.fromEntries([["a", 1]]), Dict.has("b")); // false
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
Dict.has = (key) => (m) => m.has(key);
|
|
105
|
+
/**
|
|
106
|
+
* Looks up a value by key, returning `Some(value)` if found and `None` if not.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* pipe(Dict.fromEntries([["a", 1]]), Dict.lookup("a")); // Some(1)
|
|
111
|
+
* pipe(Dict.fromEntries([["a", 1]]), Dict.lookup("b")); // None
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
Dict.lookup = (key) => (m) => m.has(key) ? Option_js_1.Option.some(m.get(key)) : Option_js_1.Option.none();
|
|
115
|
+
/**
|
|
116
|
+
* Returns the number of entries in the dictionary.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* Dict.size(Dict.fromEntries([["a", 1], ["b", 2]])); // 2
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
Dict.size = (m) => m.size;
|
|
124
|
+
/**
|
|
125
|
+
* Returns `true` if the dictionary has no entries.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* Dict.isEmpty(Dict.empty()); // true
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
Dict.isEmpty = (m) => m.size === 0;
|
|
133
|
+
/**
|
|
134
|
+
* Returns all keys as a readonly array, in insertion order.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* Dict.keys(Dict.fromEntries([["a", 1], ["b", 2]])); // ["a", "b"]
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
Dict.keys = (m) => [...m.keys()];
|
|
142
|
+
/**
|
|
143
|
+
* Returns all values as a readonly array, in insertion order.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* Dict.values(Dict.fromEntries([["a", 1], ["b", 2]])); // [1, 2]
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
Dict.values = (m) => [...m.values()];
|
|
151
|
+
/**
|
|
152
|
+
* Returns all key-value pairs as a readonly array of tuples, in insertion order.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* Dict.entries(Dict.fromEntries([["a", 1], ["b", 2]])); // [["a", 1], ["b", 2]]
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
Dict.entries = (m) => [...m.entries()];
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Modification
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
/**
|
|
164
|
+
* Returns a new dictionary with the given key set to the given value.
|
|
165
|
+
* If the key already exists, its value is replaced.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* pipe(Dict.fromEntries([["a", 1]]), Dict.insert("b", 2));
|
|
170
|
+
* // ReadonlyMap { "a" => 1, "b" => 2 }
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
Dict.insert = (key, value) => (m) => {
|
|
174
|
+
const result = new globalThis.Map(m);
|
|
175
|
+
result.set(key, value);
|
|
176
|
+
return result;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Returns a new dictionary with the given key removed.
|
|
180
|
+
* If the key does not exist, the dictionary is returned unchanged.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* pipe(Dict.fromEntries([["a", 1], ["b", 2]]), Dict.remove("a"));
|
|
185
|
+
* // ReadonlyMap { "b" => 2 }
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
Dict.remove = (key) => (m) => {
|
|
189
|
+
if (!m.has(key))
|
|
190
|
+
return m;
|
|
191
|
+
const result = new globalThis.Map(m);
|
|
192
|
+
result.delete(key);
|
|
193
|
+
return result;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Returns a new dictionary with the value at `key` set by `f`. If the key does not exist,
|
|
197
|
+
* `f` receives `None`. If the key exists, `f` receives `Some(currentValue)`.
|
|
198
|
+
*
|
|
199
|
+
* Useful for incrementing counters, initialising defaults, or conditional updates.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* import { Option } from "@nlozgachev/pipelined/core";
|
|
204
|
+
*
|
|
205
|
+
* const increment = (opt: Option<number>) => Option.getOrElse(() => 0)(opt) + 1;
|
|
206
|
+
* pipe(Dict.fromEntries([["views", 5]]), Dict.upsert("views", increment)); // { views: 6 }
|
|
207
|
+
* pipe(Dict.fromEntries([["views", 5]]), Dict.upsert("likes", increment)); // { views: 5, likes: 1 }
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
Dict.upsert = (key, f) => (m) => {
|
|
211
|
+
const result = new globalThis.Map(m);
|
|
212
|
+
result.set(key, f(Dict.lookup(key)(m)));
|
|
213
|
+
return result;
|
|
214
|
+
};
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Transform
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
/**
|
|
219
|
+
* Transforms each value in the dictionary.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* pipe(Dict.fromEntries([["a", 1], ["b", 2]]), Dict.map(n => n * 2));
|
|
224
|
+
* // ReadonlyMap { "a" => 2, "b" => 4 }
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
Dict.map = (f) => (m) => {
|
|
228
|
+
const result = new globalThis.Map();
|
|
229
|
+
for (const [k, v] of m) {
|
|
230
|
+
result.set(k, f(v));
|
|
231
|
+
}
|
|
232
|
+
return result;
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Transforms each value in the dictionary, also receiving the key.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* pipe(Dict.fromEntries([["a", 1], ["b", 2]]), Dict.mapWithKey((k, v) => `${k}:${v}`));
|
|
240
|
+
* // ReadonlyMap { "a" => "a:1", "b" => "b:2" }
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
Dict.mapWithKey = (f) => (m) => {
|
|
244
|
+
const result = new globalThis.Map();
|
|
245
|
+
for (const [k, v] of m) {
|
|
246
|
+
result.set(k, f(k, v));
|
|
247
|
+
}
|
|
248
|
+
return result;
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Returns a new dictionary containing only the entries for which the predicate returns `true`.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* pipe(Dict.fromEntries([["a", 1], ["b", 3], ["c", 0]]), Dict.filter(n => n > 0));
|
|
256
|
+
* // ReadonlyMap { "a" => 1, "b" => 3 }
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
Dict.filter = (predicate) => (m) => {
|
|
260
|
+
const result = new globalThis.Map();
|
|
261
|
+
for (const [k, v] of m) {
|
|
262
|
+
if (predicate(v))
|
|
263
|
+
result.set(k, v);
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Returns a new dictionary containing only the entries for which the predicate returns `true`.
|
|
269
|
+
* The predicate also receives the key.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```ts
|
|
273
|
+
* pipe(Dict.fromEntries([["a", 1], ["b", 2]]), Dict.filterWithKey((k, v) => k !== "a" && v > 0));
|
|
274
|
+
* // ReadonlyMap { "b" => 2 }
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
Dict.filterWithKey = (predicate) => (m) => {
|
|
278
|
+
const result = new globalThis.Map();
|
|
279
|
+
for (const [k, v] of m) {
|
|
280
|
+
if (predicate(k, v))
|
|
281
|
+
result.set(k, v);
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Removes all `None` values from a `ReadonlyMap<K, Option<A>>`, returning a plain
|
|
287
|
+
* `ReadonlyMap<K, A>`. Useful when building dictionaries from fallible lookups.
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```ts
|
|
291
|
+
* import { Option } from "@nlozgachev/pipelined/core";
|
|
292
|
+
*
|
|
293
|
+
* Dict.compact(Dict.fromEntries([
|
|
294
|
+
* ["a", Option.some(1)],
|
|
295
|
+
* ["b", Option.none()],
|
|
296
|
+
* ["c", Option.some(3)],
|
|
297
|
+
* ]));
|
|
298
|
+
* // ReadonlyMap { "a" => 1, "c" => 3 }
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
Dict.compact = (m) => {
|
|
302
|
+
const result = new globalThis.Map();
|
|
303
|
+
for (const [k, v] of m) {
|
|
304
|
+
if (v.kind === "Some")
|
|
305
|
+
result.set(k, v.value);
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
};
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
// Combine
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
/**
|
|
313
|
+
* Merges two dictionaries. When both contain the same key, the value from `other` takes
|
|
314
|
+
* precedence.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```ts
|
|
318
|
+
* pipe(
|
|
319
|
+
* Dict.fromEntries([["a", 1], ["b", 2]]),
|
|
320
|
+
* Dict.union(Dict.fromEntries([["b", 3], ["c", 4]])),
|
|
321
|
+
* );
|
|
322
|
+
* // ReadonlyMap { "a" => 1, "b" => 3, "c" => 4 }
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
Dict.union = (other) => (m) => {
|
|
326
|
+
const result = new globalThis.Map(m);
|
|
327
|
+
for (const [k, v] of other) {
|
|
328
|
+
result.set(k, v);
|
|
329
|
+
}
|
|
330
|
+
return result;
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* Returns a new dictionary containing only the entries whose keys appear in both dictionaries.
|
|
334
|
+
* Values are taken from the left (base) dictionary.
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* ```ts
|
|
338
|
+
* pipe(
|
|
339
|
+
* Dict.fromEntries([["a", 1], ["b", 2], ["c", 3]]),
|
|
340
|
+
* Dict.intersection(Dict.fromEntries([["b", 99], ["c", 0]])),
|
|
341
|
+
* );
|
|
342
|
+
* // ReadonlyMap { "b" => 2, "c" => 3 }
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
Dict.intersection = (other) => (m) => {
|
|
346
|
+
const result = new globalThis.Map();
|
|
347
|
+
for (const [k, v] of m) {
|
|
348
|
+
if (other.has(k))
|
|
349
|
+
result.set(k, v);
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
};
|
|
353
|
+
/**
|
|
354
|
+
* Returns a new dictionary containing only the entries whose keys do not appear in `other`.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* pipe(
|
|
359
|
+
* Dict.fromEntries([["a", 1], ["b", 2], ["c", 3]]),
|
|
360
|
+
* Dict.difference(Dict.fromEntries([["b", 0]])),
|
|
361
|
+
* );
|
|
362
|
+
* // ReadonlyMap { "a" => 1, "c" => 3 }
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
Dict.difference = (other) => (m) => {
|
|
366
|
+
const result = new globalThis.Map();
|
|
367
|
+
for (const [k, v] of m) {
|
|
368
|
+
if (!other.has(k))
|
|
369
|
+
result.set(k, v);
|
|
370
|
+
}
|
|
371
|
+
return result;
|
|
372
|
+
};
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
// Fold
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
/**
|
|
377
|
+
* Folds the dictionary into a single value by applying `f` to each value in insertion order.
|
|
378
|
+
* When you also need the key, use `reduceWithKey`.
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```ts
|
|
382
|
+
* Dict.reduce(0, (acc, value) => acc + value)(
|
|
383
|
+
* Dict.fromEntries([["a", 1], ["b", 2], ["c", 3]])
|
|
384
|
+
* ); // 6
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
Dict.reduce = (init, f) => (m) => {
|
|
388
|
+
let acc = init;
|
|
389
|
+
for (const v of m.values()) {
|
|
390
|
+
acc = f(acc, v);
|
|
391
|
+
}
|
|
392
|
+
return acc;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* Folds the dictionary into a single value by applying `f` to each key-value pair in insertion
|
|
396
|
+
* order.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```ts
|
|
400
|
+
* Dict.reduceWithKey("", (acc, value, key) => acc + key + ":" + value + " ")(
|
|
401
|
+
* Dict.fromEntries([["a", 1], ["b", 2]])
|
|
402
|
+
* ); // "a:1 b:2 "
|
|
403
|
+
* ```
|
|
404
|
+
*/
|
|
405
|
+
Dict.reduceWithKey = (init, f) => (m) => {
|
|
406
|
+
let acc = init;
|
|
407
|
+
for (const [k, v] of m) {
|
|
408
|
+
acc = f(acc, v, k);
|
|
409
|
+
}
|
|
410
|
+
return acc;
|
|
411
|
+
};
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
// Convert
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
/**
|
|
416
|
+
* Converts a `ReadonlyMap<string, V>` to a plain object. Only meaningful when keys are strings.
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```ts
|
|
420
|
+
* Dict.toRecord(Dict.fromEntries([["a", 1], ["b", 2]])); // { a: 1, b: 2 }
|
|
421
|
+
* ```
|
|
422
|
+
*/
|
|
423
|
+
Dict.toRecord = (m) => Object.fromEntries(m);
|
|
424
|
+
})(Dict || (exports.Dict = Dict = {}));
|