@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.
Files changed (76) hide show
  1. package/README.md +9 -3
  2. package/esm/src/Core/Option.js +2 -1
  3. package/esm/src/Core/RemoteData.js +5 -5
  4. package/esm/src/Core/TaskValidation.js +1 -3
  5. package/esm/src/Core/Tuple.js +112 -0
  6. package/esm/src/Core/index.js +4 -5
  7. package/esm/src/{Core → Utils}/Arr.js +95 -23
  8. package/esm/src/Utils/Dict.js +421 -0
  9. package/esm/src/Utils/Num.js +124 -0
  10. package/esm/src/{Core → Utils}/Rec.js +85 -11
  11. package/esm/src/Utils/Str.js +134 -0
  12. package/esm/src/Utils/Uniq.js +265 -0
  13. package/esm/src/Utils/index.js +6 -0
  14. package/package.json +11 -1
  15. package/script/src/Core/Option.js +2 -1
  16. package/script/src/Core/RemoteData.js +5 -5
  17. package/script/src/Core/TaskValidation.js +1 -3
  18. package/script/src/Core/Tuple.js +115 -0
  19. package/script/src/Core/index.js +4 -5
  20. package/script/src/{Core → Utils}/Arr.js +95 -23
  21. package/script/src/Utils/Dict.js +424 -0
  22. package/script/src/Utils/Num.js +127 -0
  23. package/script/src/{Core → Utils}/Rec.js +85 -11
  24. package/script/src/Utils/Str.js +137 -0
  25. package/script/src/Utils/Uniq.js +268 -0
  26. package/script/src/Utils/index.js +22 -0
  27. package/types/src/Composition/compose.d.ts.map +1 -1
  28. package/types/src/Composition/converge.d.ts.map +1 -1
  29. package/types/src/Composition/curry.d.ts.map +1 -1
  30. package/types/src/Composition/flow.d.ts.map +1 -1
  31. package/types/src/Composition/fn.d.ts.map +1 -1
  32. package/types/src/Composition/juxt.d.ts.map +1 -1
  33. package/types/src/Composition/memoize.d.ts.map +1 -1
  34. package/types/src/Composition/not.d.ts.map +1 -1
  35. package/types/src/Composition/on.d.ts.map +1 -1
  36. package/types/src/Composition/pipe.d.ts.map +1 -1
  37. package/types/src/Composition/uncurry.d.ts.map +1 -1
  38. package/types/src/Core/Deferred.d.ts.map +1 -1
  39. package/types/src/Core/Lens.d.ts.map +1 -1
  40. package/types/src/Core/Logged.d.ts.map +1 -1
  41. package/types/src/Core/Option.d.ts.map +1 -1
  42. package/types/src/Core/Optional.d.ts.map +1 -1
  43. package/types/src/Core/Predicate.d.ts.map +1 -1
  44. package/types/src/Core/Reader.d.ts.map +1 -1
  45. package/types/src/Core/Refinement.d.ts.map +1 -1
  46. package/types/src/Core/RemoteData.d.ts.map +1 -1
  47. package/types/src/Core/Result.d.ts.map +1 -1
  48. package/types/src/Core/State.d.ts.map +1 -1
  49. package/types/src/Core/Task.d.ts.map +1 -1
  50. package/types/src/Core/TaskOption.d.ts.map +1 -1
  51. package/types/src/Core/TaskResult.d.ts.map +1 -1
  52. package/types/src/Core/TaskValidation.d.ts.map +1 -1
  53. package/types/src/Core/These.d.ts.map +1 -1
  54. package/types/src/Core/Tuple.d.ts +129 -0
  55. package/types/src/Core/Tuple.d.ts.map +1 -0
  56. package/types/src/Core/Validation.d.ts.map +1 -1
  57. package/types/src/Core/index.d.ts +4 -5
  58. package/types/src/Core/index.d.ts.map +1 -1
  59. package/types/src/Types/Brand.d.ts.map +1 -1
  60. package/types/src/Types/NonEmptyList.d.ts.map +1 -1
  61. package/types/src/{Core → Utils}/Arr.d.ts +25 -3
  62. package/types/src/Utils/Arr.d.ts.map +1 -0
  63. package/types/src/Utils/Dict.d.ts +310 -0
  64. package/types/src/Utils/Dict.d.ts.map +1 -0
  65. package/types/src/Utils/Num.d.ts +110 -0
  66. package/types/src/Utils/Num.d.ts.map +1 -0
  67. package/types/src/{Core → Utils}/Rec.d.ts +39 -1
  68. package/types/src/Utils/Rec.d.ts.map +1 -0
  69. package/types/src/Utils/Str.d.ts +128 -0
  70. package/types/src/Utils/Str.d.ts.map +1 -0
  71. package/types/src/Utils/Uniq.d.ts +179 -0
  72. package/types/src/Utils/Uniq.d.ts.map +1 -0
  73. package/types/src/Utils/index.d.ts +7 -0
  74. package/types/src/Utils/index.d.ts.map +1 -0
  75. package/types/src/Core/Arr.d.ts.map +0 -1
  76. 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("./Deferred.js");
5
- const Option_js_1 = require("./Option.js");
6
- const Result_js_1 = require("./Result.js");
7
- const Task_js_1 = require("./Task.js");
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) => data.map(f);
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) => data.filter(predicate);
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.push([data[i], other[i]]);
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.push(f(data[i], other[i]));
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) => [].concat(...data.map(f));
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 result = [];
320
- for (const a of data) {
321
- const mapped = f(a);
322
- if (Option_js_1.Option.isNone(mapped))
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.push(mapped.value);
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 result = [];
342
- for (const a of data) {
343
- const mapped = f(a);
344
- if (Result_js_1.Result.isErr(mapped))
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.push(mapped.value);
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) => data.some(predicate);
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) => data.every(predicate);
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 = {}));