@nlozgachev/pipelined 0.65.0 → 0.67.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 +70 -57
- package/dist/{Core-BHN87zoB.mjs → Core-BCN6WRzp.mjs} +1038 -616
- package/dist/{Core-BDXRLmMa.cjs → Core-CfAL_21H.cjs} +1043 -621
- package/dist/{InternalTypes-DzDey5Do.d.ts → InternalTypes-C0F5jyVQ.d.ts} +1 -1
- package/dist/{InternalTypes-B1Lh9uw_.d.cts → InternalTypes-DxSlzrpu.d.cts} +1 -1
- package/dist/composition.d.cts +2 -2
- package/dist/composition.d.ts +2 -2
- package/dist/core.cjs +2 -2
- package/dist/core.d.cts +3 -3
- package/dist/core.d.ts +3 -3
- package/dist/core.mjs +2 -2
- package/dist/data.cjs +1040 -535
- package/dist/data.d.cts +497 -310
- package/dist/data.d.ts +497 -310
- package/dist/data.mjs +1040 -535
- package/dist/{index-Bs8En5LJ.d.cts → index-CTe9CCHY.d.cts} +3 -0
- package/dist/{index-Bs8En5LJ.d.ts → index-CTe9CCHY.d.ts} +3 -0
- package/dist/{index-B07Wr815.d.ts → index-CdzHrj62.d.ts} +792 -444
- package/dist/{index-DCK_VPog.d.cts → index-NYjtX7XS.d.cts} +792 -444
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/types.cjs +5 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.mjs +5 -1
- package/package.json +20 -19
package/dist/data.mjs
CHANGED
|
@@ -1,50 +1,93 @@
|
|
|
1
|
-
import { S as Deferred,
|
|
1
|
+
import { S as Deferred, g as Maybe, i as isNonEmptyArr, n as Task, o as Result, r as Validation } from "./Core-BCN6WRzp.mjs";
|
|
2
2
|
//#region src/Data/Arr.ts
|
|
3
3
|
let ArrMaybe;
|
|
4
4
|
(function(_ArrMaybe) {
|
|
5
|
-
const traverse = _ArrMaybe.traverse = (
|
|
6
|
-
const n =
|
|
5
|
+
const traverse = _ArrMaybe.traverse = (transform) => (items) => {
|
|
6
|
+
const n = items.length;
|
|
7
7
|
const result = new Array(n);
|
|
8
8
|
for (let i = 0; i < n; i++) {
|
|
9
|
-
const mapped =
|
|
9
|
+
const mapped = transform(items[i]);
|
|
10
10
|
if (mapped.kind === "None") return Maybe.make.none();
|
|
11
11
|
result[i] = mapped.value;
|
|
12
12
|
}
|
|
13
13
|
return Maybe.make.some(result);
|
|
14
14
|
};
|
|
15
|
-
_ArrMaybe.sequence = (
|
|
15
|
+
_ArrMaybe.sequence = (items) => traverse((item) => item)(items);
|
|
16
16
|
})(ArrMaybe || (ArrMaybe = {}));
|
|
17
17
|
let ArrResult;
|
|
18
18
|
(function(_ArrResult) {
|
|
19
|
-
const traverse = _ArrResult.traverse = (
|
|
20
|
-
const n =
|
|
19
|
+
const traverse = _ArrResult.traverse = (transform) => (items) => {
|
|
20
|
+
const n = items.length;
|
|
21
21
|
const result = new Array(n);
|
|
22
22
|
for (let i = 0; i < n; i++) {
|
|
23
|
-
const mapped =
|
|
23
|
+
const mapped = transform(items[i]);
|
|
24
24
|
if (mapped.kind === "Err") return mapped;
|
|
25
25
|
result[i] = mapped.value;
|
|
26
26
|
}
|
|
27
27
|
return Result.make.ok(result);
|
|
28
28
|
};
|
|
29
|
-
_ArrResult.sequence = (
|
|
29
|
+
_ArrResult.sequence = (items) => traverse((item) => item)(items);
|
|
30
30
|
})(ArrResult || (ArrResult = {}));
|
|
31
|
+
let ArrValidation;
|
|
32
|
+
(function(_ArrValidation) {
|
|
33
|
+
const traverse = _ArrValidation.traverse = (transform) => (items) => {
|
|
34
|
+
const n = items.length;
|
|
35
|
+
const result = new Array(n);
|
|
36
|
+
const errors = [];
|
|
37
|
+
for (let i = 0; i < n; i++) {
|
|
38
|
+
const mapped = transform(items[i]);
|
|
39
|
+
if (Validation.is.failed(mapped)) errors.push(...mapped.errors);
|
|
40
|
+
else if (errors.length === 0) result[i] = mapped.value;
|
|
41
|
+
}
|
|
42
|
+
return isNonEmptyArr(errors) ? Validation.make.failedAll(errors) : Validation.make.passed(result);
|
|
43
|
+
};
|
|
44
|
+
_ArrValidation.sequence = (items) => traverse((item) => item)(items);
|
|
45
|
+
})(ArrValidation || (ArrValidation = {}));
|
|
31
46
|
let ArrTaskResult;
|
|
32
47
|
(function(_ArrTaskResult) {
|
|
33
|
-
const traverse = _ArrTaskResult.traverse = (
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
const traverse = _ArrTaskResult.traverse = (transform, options) => (items) => (signal) => Deferred.from.Promise((async () => {
|
|
49
|
+
const concurrency = options?.concurrency;
|
|
50
|
+
const len = items.length;
|
|
51
|
+
if (concurrency === void 0 || concurrency <= 1 || len <= 1) {
|
|
52
|
+
const result = [];
|
|
53
|
+
for (const a of items) {
|
|
54
|
+
const r = await Deferred.to.Promise(transform(a)(signal));
|
|
55
|
+
if (Result.is.err(r)) return r;
|
|
56
|
+
result.push(r.value);
|
|
57
|
+
}
|
|
58
|
+
return Result.make.ok(result);
|
|
39
59
|
}
|
|
40
|
-
return
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
const results = new Array(len);
|
|
62
|
+
let nextIndex = 0;
|
|
63
|
+
let settled = false;
|
|
64
|
+
const worker = async () => {
|
|
65
|
+
while (nextIndex < len && !settled) {
|
|
66
|
+
const currentIndex = nextIndex++;
|
|
67
|
+
const r = await Deferred.to.Promise(transform(items[currentIndex])(signal));
|
|
68
|
+
if (settled) return;
|
|
69
|
+
if (Result.is.err(r)) {
|
|
70
|
+
settled = true;
|
|
71
|
+
resolve(r);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
results[currentIndex] = r.value;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const workerCount = Math.min(concurrency, len);
|
|
78
|
+
const workers = [];
|
|
79
|
+
for (let i = 0; i < workerCount; i++) workers.push(worker());
|
|
80
|
+
Promise.all(workers).then(() => {
|
|
81
|
+
if (!settled) resolve(Result.make.ok(results));
|
|
82
|
+
});
|
|
83
|
+
});
|
|
41
84
|
})());
|
|
42
|
-
_ArrTaskResult.sequence = (
|
|
85
|
+
_ArrTaskResult.sequence = (items) => traverse((item) => item)(items);
|
|
43
86
|
})(ArrTaskResult || (ArrTaskResult = {}));
|
|
44
87
|
let ArrTask;
|
|
45
88
|
(function(_ArrTask) {
|
|
46
|
-
const traverse = _ArrTask.traverse = (
|
|
47
|
-
_ArrTask.sequence = (
|
|
89
|
+
const traverse = _ArrTask.traverse = (transform, options) => (items) => Task.all(items.map(transform), options);
|
|
90
|
+
_ArrTask.sequence = (items) => traverse((item) => item)(items);
|
|
48
91
|
_ArrTask.Result = ArrTaskResult;
|
|
49
92
|
})(ArrTask || (ArrTask = {}));
|
|
50
93
|
/**
|
|
@@ -74,96 +117,118 @@ let ArrTask;
|
|
|
74
117
|
/**
|
|
75
118
|
* Returns the first element of an array, or None if the array is empty.
|
|
76
119
|
*
|
|
120
|
+
* @see {@link last} for accessing the final element.
|
|
121
|
+
* @see {@link tail} for all elements except the first.
|
|
122
|
+
*
|
|
77
123
|
* @example
|
|
78
124
|
* ```ts
|
|
79
125
|
* Arr.head([1, 2, 3]); // Some(1)
|
|
80
126
|
* Arr.head([]); // None
|
|
81
127
|
* ```
|
|
82
128
|
*/
|
|
83
|
-
const head = (
|
|
129
|
+
const head = (items) => items.length > 0 ? Maybe.make.some(items[0]) : Maybe.make.none();
|
|
84
130
|
/**
|
|
85
131
|
* Returns the last element of an array, or None if the array is empty.
|
|
86
132
|
*
|
|
133
|
+
* @see {@link head} for accessing the first element.
|
|
134
|
+
* @see {@link init} for all elements except the last.
|
|
135
|
+
*
|
|
87
136
|
* @example
|
|
88
137
|
* ```ts
|
|
89
138
|
* Arr.last([1, 2, 3]); // Some(3)
|
|
90
139
|
* Arr.last([]); // None
|
|
91
140
|
* ```
|
|
92
141
|
*/
|
|
93
|
-
const last = (
|
|
142
|
+
const last = (items) => items.length > 0 ? Maybe.make.some(items[items.length - 1]) : Maybe.make.none();
|
|
94
143
|
/**
|
|
95
144
|
* Returns all elements except the first, or None if the array is empty.
|
|
96
145
|
*
|
|
146
|
+
* @see {@link head} for accessing the first element.
|
|
147
|
+
*
|
|
97
148
|
* @example
|
|
98
149
|
* ```ts
|
|
99
150
|
* Arr.tail([1, 2, 3]); // Some([2, 3])
|
|
100
151
|
* Arr.tail([]); // None
|
|
101
152
|
* ```
|
|
102
153
|
*/
|
|
103
|
-
const tail = (
|
|
154
|
+
const tail = (items) => items.length > 0 ? Maybe.make.some(items.slice(1)) : Maybe.make.none();
|
|
104
155
|
/**
|
|
105
156
|
* Returns all elements except the last, or None if the array is empty.
|
|
106
157
|
*
|
|
158
|
+
* @see {@link last} for accessing the final element.
|
|
159
|
+
*
|
|
107
160
|
* @example
|
|
108
161
|
* ```ts
|
|
109
162
|
* Arr.init([1, 2, 3]); // Some([1, 2])
|
|
110
163
|
* Arr.init([]); // None
|
|
111
164
|
* ```
|
|
112
165
|
*/
|
|
113
|
-
const init = (
|
|
166
|
+
const init = (items) => items.length > 0 ? Maybe.make.some(items.slice(0, -1)) : Maybe.make.none();
|
|
114
167
|
/**
|
|
115
168
|
* Returns the first element matching the predicate, or None.
|
|
116
169
|
*
|
|
170
|
+
* @see {@link findLast} for finding the last matching element.
|
|
171
|
+
* @see {@link findIndex} for obtaining the index of the first matching element.
|
|
172
|
+
*
|
|
117
173
|
* @example
|
|
118
174
|
* ```ts
|
|
119
175
|
* pipe([1, 2, 3, 4], Arr.findFirst(n => n > 2)); // Some(3)
|
|
120
176
|
* ```
|
|
121
177
|
*/
|
|
122
|
-
const findFirst = (predicate) => (
|
|
123
|
-
const idx =
|
|
124
|
-
return idx !== -1 ? Maybe.make.some(
|
|
178
|
+
const findFirst = (predicate) => (items) => {
|
|
179
|
+
const idx = items.findIndex(predicate);
|
|
180
|
+
return idx !== -1 ? Maybe.make.some(items[idx]) : Maybe.make.none();
|
|
125
181
|
};
|
|
126
182
|
/**
|
|
127
183
|
* Returns the last element matching the predicate, or None.
|
|
128
184
|
*
|
|
185
|
+
* @see {@link findFirst} for finding the first matching element.
|
|
186
|
+
*
|
|
129
187
|
* @example
|
|
130
188
|
* ```ts
|
|
131
189
|
* pipe([1, 2, 3, 4], Arr.findLast(n => n > 2)); // Some(4)
|
|
132
190
|
* ```
|
|
133
191
|
*/
|
|
134
|
-
const findLast = (predicate) => (
|
|
135
|
-
for (let i =
|
|
192
|
+
const findLast = (predicate) => (items) => {
|
|
193
|
+
for (let i = items.length - 1; i >= 0; i--) if (predicate(items[i])) return Maybe.make.some(items[i]);
|
|
136
194
|
return Maybe.make.none();
|
|
137
195
|
};
|
|
138
196
|
/**
|
|
139
197
|
* Returns the index of the first element matching the predicate, or None.
|
|
140
198
|
*
|
|
199
|
+
* @see {@link findFirst} for finding the matching element value.
|
|
200
|
+
*
|
|
141
201
|
* @example
|
|
142
202
|
* ```ts
|
|
143
203
|
* pipe([1, 2, 3, 4], Arr.findIndex(n => n > 2)); // Some(2)
|
|
144
204
|
* ```
|
|
145
205
|
*/
|
|
146
|
-
const findIndex = (predicate) => (
|
|
147
|
-
const idx =
|
|
206
|
+
const findIndex = (predicate) => (items) => {
|
|
207
|
+
const idx = items.findIndex(predicate);
|
|
148
208
|
return idx !== -1 ? Maybe.make.some(idx) : Maybe.make.none();
|
|
149
209
|
};
|
|
150
210
|
/**
|
|
151
211
|
* Transforms each element of an array.
|
|
152
212
|
*
|
|
213
|
+
* @see {@link flatMap} for mapping and flattening arrays.
|
|
214
|
+
* @see {@link mapWithIndex} for mapping with element indices.
|
|
215
|
+
*
|
|
153
216
|
* @example
|
|
154
217
|
* ```ts
|
|
155
218
|
* pipe([1, 2, 3], Arr.map(n => n * 2)); // [2, 4, 6]
|
|
156
219
|
* ```
|
|
157
220
|
*/
|
|
158
|
-
const map$3 = (
|
|
159
|
-
const n =
|
|
221
|
+
const map$3 = (transform) => (items) => {
|
|
222
|
+
const n = items.length;
|
|
160
223
|
const result = new Array(n);
|
|
161
|
-
for (let i = 0; i < n; i++) result[i] =
|
|
224
|
+
for (let i = 0; i < n; i++) result[i] = transform(items[i]);
|
|
162
225
|
return result;
|
|
163
226
|
};
|
|
164
227
|
/**
|
|
165
228
|
* Transforms each element using both its value and its zero-based index.
|
|
166
229
|
*
|
|
230
|
+
* @see {@link map} for mapping without indices.
|
|
231
|
+
*
|
|
167
232
|
* @example
|
|
168
233
|
* ```ts
|
|
169
234
|
* pipe(
|
|
@@ -172,30 +237,34 @@ const map$3 = (f) => (data) => {
|
|
|
172
237
|
* ); // [{ position: 1, value: "a" }, { position: 2, value: "b" }, { position: 3, value: "c" }]
|
|
173
238
|
* ```
|
|
174
239
|
*/
|
|
175
|
-
const mapWithIndex = (
|
|
176
|
-
const n =
|
|
240
|
+
const mapWithIndex = (transform) => (items) => {
|
|
241
|
+
const n = items.length;
|
|
177
242
|
const result = new Array(n);
|
|
178
|
-
for (let i = 0; i < n; i++) result[i] =
|
|
243
|
+
for (let i = 0; i < n; i++) result[i] = transform(i, items[i]);
|
|
179
244
|
return result;
|
|
180
245
|
};
|
|
181
246
|
/**
|
|
182
247
|
* Filters elements that satisfy the predicate.
|
|
183
248
|
*
|
|
249
|
+
* @see {@link filterMap} for filtering and mapping simultaneously.
|
|
250
|
+
*
|
|
184
251
|
* @example
|
|
185
252
|
* ```ts
|
|
186
253
|
* pipe([1, 2, 3, 4], Arr.filter(n => n % 2 === 0)); // [2, 4]
|
|
187
254
|
* ```
|
|
188
255
|
*/
|
|
189
|
-
const filter$3 = (predicate) => (
|
|
190
|
-
const n =
|
|
256
|
+
const filter$3 = (predicate) => (items) => {
|
|
257
|
+
const n = items.length;
|
|
191
258
|
const result = [];
|
|
192
|
-
for (let i = 0; i < n; i++) if (predicate(
|
|
259
|
+
for (let i = 0; i < n; i++) if (predicate(items[i])) result.push(items[i]);
|
|
193
260
|
return result;
|
|
194
261
|
};
|
|
195
262
|
/**
|
|
196
263
|
* Maps each element to a Maybe and collects only the Some values.
|
|
197
264
|
* Combines map and filter in a single pass.
|
|
198
265
|
*
|
|
266
|
+
* @see {@link filter} for filtering with a boolean predicate.
|
|
267
|
+
*
|
|
199
268
|
* @example
|
|
200
269
|
* ```ts
|
|
201
270
|
* const parseNum = (s: string): Maybe<number> => {
|
|
@@ -206,10 +275,10 @@ const filter$3 = (predicate) => (data) => {
|
|
|
206
275
|
* pipe(["1", "abc", "3"], Arr.filterMap(parseNum)); // [1, 3]
|
|
207
276
|
* ```
|
|
208
277
|
*/
|
|
209
|
-
const filterMap$3 = (
|
|
278
|
+
const filterMap$3 = (transform) => (items) => {
|
|
210
279
|
const result = [];
|
|
211
|
-
for (let i = 0; i <
|
|
212
|
-
const mapped =
|
|
280
|
+
for (let i = 0; i < items.length; i++) {
|
|
281
|
+
const mapped = transform(items[i]);
|
|
213
282
|
if (mapped.kind === "Some") result.push(mapped.value);
|
|
214
283
|
}
|
|
215
284
|
return result;
|
|
@@ -219,50 +288,59 @@ const filterMap$3 = (f) => (data) => {
|
|
|
219
288
|
* First group contains elements that satisfy the predicate,
|
|
220
289
|
* second group contains the rest.
|
|
221
290
|
*
|
|
291
|
+
* @see {@link partitionMap} for partitioning with a Result function.
|
|
292
|
+
*
|
|
222
293
|
* @example
|
|
223
294
|
* ```ts
|
|
224
295
|
* pipe([1, 2, 3, 4], Arr.partition(n => n % 2 === 0)); // [[2, 4], [1, 3]]
|
|
225
296
|
* ```
|
|
226
297
|
*/
|
|
227
|
-
const partition = (predicate) => (
|
|
298
|
+
const partition = (predicate) => (items) => {
|
|
228
299
|
const pass = [];
|
|
229
300
|
const fail = [];
|
|
230
|
-
for (const
|
|
301
|
+
for (const item of items) (predicate(item) ? pass : fail).push(item);
|
|
231
302
|
return [pass, fail];
|
|
232
303
|
};
|
|
233
304
|
/**
|
|
234
305
|
* Narrows a list of Maybe values down to a list of their underlying values,
|
|
235
306
|
* discarding all None instances.
|
|
236
307
|
*
|
|
308
|
+
* @see {@link separate} for dividing Results into error and success arrays.
|
|
309
|
+
*
|
|
237
310
|
* @example
|
|
238
311
|
* ```ts
|
|
239
312
|
* Arr.compact([Maybe.make.some(1), Maybe.make.none(), Maybe.make.some(3)]); // [1, 3]
|
|
240
313
|
* ```
|
|
241
314
|
*/
|
|
242
|
-
const compact$2 = (
|
|
315
|
+
const compact$2 = (items) => {
|
|
243
316
|
const result = [];
|
|
244
|
-
for (const item of
|
|
317
|
+
for (const item of items) if (item.kind === "Some") result.push(item.value);
|
|
245
318
|
return result;
|
|
246
319
|
};
|
|
247
320
|
/**
|
|
248
321
|
* Separates an array of Result values into two separate lists of errors and successes.
|
|
249
322
|
* Returns a tuple containing `[errors, successes]`.
|
|
250
323
|
*
|
|
324
|
+
* @see {@link compact} for extracting Some values from Maybe instances.
|
|
325
|
+
*
|
|
251
326
|
* @example
|
|
252
327
|
* ```ts
|
|
253
328
|
* Arr.separate([Result.make.ok(1), Result.make.err("bad"), Result.make.ok(3)]); // [["bad"], [1, 3]]
|
|
254
329
|
* ```
|
|
255
330
|
*/
|
|
256
|
-
const separate = (
|
|
331
|
+
const separate = (items) => {
|
|
257
332
|
const errors = [];
|
|
258
333
|
const successes = [];
|
|
259
|
-
for (const item of
|
|
334
|
+
for (const item of items) if (item.kind === "Ok") successes.push(item.value);
|
|
260
335
|
else errors.push(item.error);
|
|
261
336
|
return [errors, successes];
|
|
262
337
|
};
|
|
263
338
|
/**
|
|
264
339
|
* Maps each element to a Result, and separates the results into a tuple of failures and successes.
|
|
265
340
|
*
|
|
341
|
+
* @see {@link partition} for partitioning with a boolean predicate.
|
|
342
|
+
* @see {@link partitionMaybe} for partitioning with a Maybe function.
|
|
343
|
+
*
|
|
266
344
|
* @example
|
|
267
345
|
* ```ts
|
|
268
346
|
* pipe(
|
|
@@ -271,11 +349,11 @@ const separate = (data) => {
|
|
|
271
349
|
* ); // [["odd: 1", "odd: 3"], [2, 4]]
|
|
272
350
|
* ```
|
|
273
351
|
*/
|
|
274
|
-
const partitionMap = (
|
|
352
|
+
const partitionMap = (transform) => (items) => {
|
|
275
353
|
const errors = [];
|
|
276
354
|
const successes = [];
|
|
277
|
-
for (const item of
|
|
278
|
-
const mapped =
|
|
355
|
+
for (const item of items) {
|
|
356
|
+
const mapped = transform(item);
|
|
279
357
|
if (mapped.kind === "Ok") successes.push(mapped.value);
|
|
280
358
|
else errors.push(mapped.error);
|
|
281
359
|
}
|
|
@@ -284,6 +362,8 @@ const partitionMap = (f) => (data) => {
|
|
|
284
362
|
/**
|
|
285
363
|
* Groups elements by a key function.
|
|
286
364
|
*
|
|
365
|
+
* @see {@link indexBy} for indexing elements into a Map.
|
|
366
|
+
*
|
|
287
367
|
* @example
|
|
288
368
|
* ```ts
|
|
289
369
|
* pipe(
|
|
@@ -292,27 +372,33 @@ const partitionMap = (f) => (data) => {
|
|
|
292
372
|
* ); // { a: ["apple", "avocado"], b: ["banana"] }
|
|
293
373
|
* ```
|
|
294
374
|
*/
|
|
295
|
-
const groupBy$2 = (
|
|
375
|
+
const groupBy$2 = (keySelector) => (items) => {
|
|
296
376
|
const result = {};
|
|
297
|
-
for (const
|
|
298
|
-
const key =
|
|
377
|
+
for (const item of items) {
|
|
378
|
+
const key = keySelector(item);
|
|
299
379
|
if (!result[key]) result[key] = [];
|
|
300
|
-
result[key].push(
|
|
380
|
+
result[key].push(item);
|
|
301
381
|
}
|
|
302
382
|
return result;
|
|
303
383
|
};
|
|
304
384
|
/**
|
|
305
385
|
* Removes duplicate elements using strict equality.
|
|
306
386
|
*
|
|
387
|
+
* @see {@link uniqBy} for deduplicating by a key projection.
|
|
388
|
+
* @see {@link uniqWith} for deduplicating with custom equality.
|
|
389
|
+
*
|
|
307
390
|
* @example
|
|
308
391
|
* ```ts
|
|
309
392
|
* Arr.uniq([1, 2, 2, 3, 1]); // [1, 2, 3]
|
|
310
393
|
* ```
|
|
311
394
|
*/
|
|
312
|
-
const uniq = (
|
|
395
|
+
const uniq = (items) => items.length <= 1 ? items : [...new Set(items)];
|
|
313
396
|
/**
|
|
314
397
|
* Removes duplicate elements by comparing the result of a key function.
|
|
315
398
|
*
|
|
399
|
+
* @see {@link uniq} for reference-equality deduplication.
|
|
400
|
+
* @see {@link uniqWith} for deduplicating with custom equality.
|
|
401
|
+
*
|
|
316
402
|
* @example
|
|
317
403
|
* ```ts
|
|
318
404
|
* pipe(
|
|
@@ -321,14 +407,14 @@ const uniq = (data) => data.length <= 1 ? data : [...new Set(data)];
|
|
|
321
407
|
* ); // [{id: 1, name: "a"}, {id: 2, name: "c"}]
|
|
322
408
|
* ```
|
|
323
409
|
*/
|
|
324
|
-
const uniqBy = (
|
|
410
|
+
const uniqBy = (keySelector) => (items) => {
|
|
325
411
|
const seen = /* @__PURE__ */ new Set();
|
|
326
412
|
const result = [];
|
|
327
|
-
for (const
|
|
328
|
-
const key =
|
|
413
|
+
for (const item of items) {
|
|
414
|
+
const key = keySelector(item);
|
|
329
415
|
if (!seen.has(key)) {
|
|
330
416
|
seen.add(key);
|
|
331
|
-
result.push(
|
|
417
|
+
result.push(item);
|
|
332
418
|
}
|
|
333
419
|
}
|
|
334
420
|
return result;
|
|
@@ -338,6 +424,9 @@ const uniqBy = (f) => (data) => {
|
|
|
338
424
|
* Preserves the order of first occurrences. Complements `uniq` (reference equality)
|
|
339
425
|
* and `uniqBy` (key extraction).
|
|
340
426
|
*
|
|
427
|
+
* @see {@link uniq} for reference-equality deduplication.
|
|
428
|
+
* @see {@link uniqBy} for key-based deduplication.
|
|
429
|
+
*
|
|
341
430
|
* @example
|
|
342
431
|
* ```ts
|
|
343
432
|
* type Point = { x: number; y: number };
|
|
@@ -349,29 +438,33 @@ const uniqBy = (f) => (data) => {
|
|
|
349
438
|
* ); // [{ x: 1, y: 1 }, { x: 2, y: 2 }]
|
|
350
439
|
* ```
|
|
351
440
|
*/
|
|
352
|
-
const uniqWith = (
|
|
441
|
+
const uniqWith = (areEqual) => (items) => {
|
|
353
442
|
const result = [];
|
|
354
|
-
for (const
|
|
443
|
+
for (const item of items) if (!result.some((existing) => areEqual(existing, item))) result.push(item);
|
|
355
444
|
return result;
|
|
356
445
|
};
|
|
357
446
|
/**
|
|
358
447
|
* Sorts an array using a comparison function. Returns a new array.
|
|
359
448
|
* To sort with a typed `Ordering<A>`, prefer `Arr.sortWith`.
|
|
360
449
|
*
|
|
450
|
+
* @see {@link sortWith} for sorting using typed Ordering instances.
|
|
451
|
+
*
|
|
361
452
|
* @example
|
|
362
453
|
* ```ts
|
|
363
454
|
* pipe([3, 1, 2], Arr.sortBy((a, b) => a - b)); // [1, 2, 3]
|
|
364
455
|
* ```
|
|
365
456
|
*/
|
|
366
|
-
const sortBy = (compare) => (
|
|
367
|
-
const arr =
|
|
457
|
+
const sortBy = (compare) => (items) => {
|
|
458
|
+
const arr = items;
|
|
368
459
|
if (typeof arr.toSorted === "function") return arr.toSorted(compare);
|
|
369
|
-
return [...
|
|
460
|
+
return [...items].sort(compare);
|
|
370
461
|
};
|
|
371
462
|
/**
|
|
372
463
|
* Sorts an array using an `Ordering<A>`. Returns a new array without mutating the original.
|
|
373
464
|
* Use this over `sortBy` when you have a typed `Ordering<A>` from the `Ordering` module.
|
|
374
465
|
*
|
|
466
|
+
* @see {@link sortBy} for sorting using a comparator function.
|
|
467
|
+
*
|
|
375
468
|
* @example
|
|
376
469
|
* ```ts
|
|
377
470
|
* pipe([3, 1, 2], Arr.sortWith(Ordering.number)); // [1, 2, 3]
|
|
@@ -382,37 +475,41 @@ const sortBy = (compare) => (data) => {
|
|
|
382
475
|
* pipe(products, Arr.sortWith(byPrice));
|
|
383
476
|
* ```
|
|
384
477
|
*/
|
|
385
|
-
const sortWith = (
|
|
386
|
-
const arr =
|
|
387
|
-
if (typeof arr.toSorted === "function") return arr.toSorted(
|
|
388
|
-
return [...
|
|
478
|
+
const sortWith = (ordering) => (items) => {
|
|
479
|
+
const arr = items;
|
|
480
|
+
if (typeof arr.toSorted === "function") return arr.toSorted(ordering);
|
|
481
|
+
return [...items].sort(ordering);
|
|
389
482
|
};
|
|
390
483
|
/**
|
|
391
484
|
* Pairs up elements from two arrays. Stops at the shorter array.
|
|
392
485
|
*
|
|
486
|
+
* @see {@link zipWith} for pairing elements with a custom combining function.
|
|
487
|
+
*
|
|
393
488
|
* @example
|
|
394
489
|
* ```ts
|
|
395
490
|
* pipe([1, 2, 3], Arr.zip(["a", "b"])); // [[1, "a"], [2, "b"]]
|
|
396
491
|
* ```
|
|
397
492
|
*/
|
|
398
|
-
const zip = (other) => (
|
|
399
|
-
const len = Math.min(
|
|
493
|
+
const zip = (other) => (items) => {
|
|
494
|
+
const len = Math.min(items.length, other.length);
|
|
400
495
|
const result = new Array(len);
|
|
401
|
-
for (let i = 0; i < len; i++) result[i] = [
|
|
496
|
+
for (let i = 0; i < len; i++) result[i] = [items[i], other[i]];
|
|
402
497
|
return result;
|
|
403
498
|
};
|
|
404
499
|
/**
|
|
405
500
|
* Combines elements from two arrays using a function. Stops at the shorter array.
|
|
406
501
|
*
|
|
502
|
+
* @see {@link zip} for pairing elements into 2-tuples.
|
|
503
|
+
*
|
|
407
504
|
* @example
|
|
408
505
|
* ```ts
|
|
409
506
|
* pipe([1, 2], Arr.zipWith((a: number, b: string) => `${a}${b}`)(["a", "b"])); // ["1a", "2b"]
|
|
410
507
|
* ```
|
|
411
508
|
*/
|
|
412
|
-
const zipWith = (
|
|
413
|
-
const len = Math.min(
|
|
509
|
+
const zipWith = (combine) => (other) => (items) => {
|
|
510
|
+
const len = Math.min(items.length, other.length);
|
|
414
511
|
const result = new Array(len);
|
|
415
|
-
for (let i = 0; i < len; i++) result[i] =
|
|
512
|
+
for (let i = 0; i < len; i++) result[i] = combine(items[i], other[i]);
|
|
416
513
|
return result;
|
|
417
514
|
};
|
|
418
515
|
/**
|
|
@@ -423,10 +520,10 @@ const zipWith = (f) => (other) => (data) => {
|
|
|
423
520
|
* pipe([1, 2, 3], Arr.intersperse(0)); // [1, 0, 2, 0, 3]
|
|
424
521
|
* ```
|
|
425
522
|
*/
|
|
426
|
-
const intersperse = (
|
|
427
|
-
if (
|
|
428
|
-
const result = [
|
|
429
|
-
for (let i = 1; i <
|
|
523
|
+
const intersperse = (separator) => (items) => {
|
|
524
|
+
if (items.length <= 1) return items;
|
|
525
|
+
const result = [items[0]];
|
|
526
|
+
for (let i = 1; i < items.length; i++) result.push(separator, items[i]);
|
|
430
527
|
return result;
|
|
431
528
|
};
|
|
432
529
|
/**
|
|
@@ -437,37 +534,41 @@ const intersperse = (sep) => (data) => {
|
|
|
437
534
|
* pipe([1, 2], Arr.concat([3, 4])); // [1, 2, 3, 4]
|
|
438
535
|
* ```
|
|
439
536
|
*/
|
|
440
|
-
const concat = (other) => (
|
|
537
|
+
const concat = (other) => (items) => [...items, ...other];
|
|
441
538
|
/**
|
|
442
539
|
* Splits an array into chunks of the given size.
|
|
443
540
|
*
|
|
541
|
+
* @see {@link chunkBy} for grouping consecutive elements sharing a key.
|
|
542
|
+
*
|
|
444
543
|
* @example
|
|
445
544
|
* ```ts
|
|
446
545
|
* pipe([1, 2, 3, 4, 5], Arr.chunksOf(2)); // [[1, 2], [3, 4], [5]]
|
|
447
546
|
* ```
|
|
448
547
|
*/
|
|
449
|
-
const chunksOf = (
|
|
450
|
-
if (
|
|
548
|
+
const chunksOf = (size) => (items) => {
|
|
549
|
+
if (size <= 0) return [];
|
|
451
550
|
const result = [];
|
|
452
|
-
for (let i = 0; i <
|
|
551
|
+
for (let i = 0; i < items.length; i += size) result.push(items.slice(i, i + size));
|
|
453
552
|
return result;
|
|
454
553
|
};
|
|
455
554
|
/**
|
|
456
555
|
* Flattens a nested array by one level.
|
|
457
556
|
*
|
|
557
|
+
* @see {@link flatMap} for mapping elements to arrays before flattening.
|
|
558
|
+
*
|
|
458
559
|
* @example
|
|
459
560
|
* ```ts
|
|
460
561
|
* Arr.flatten([[1, 2], [3], [4, 5]]); // [1, 2, 3, 4, 5]
|
|
461
562
|
* ```
|
|
462
563
|
*/
|
|
463
|
-
const flatten = (
|
|
564
|
+
const flatten = (items) => {
|
|
464
565
|
let totalLen = 0;
|
|
465
|
-
const outerLen =
|
|
466
|
-
for (let i = 0; i < outerLen; i++) totalLen +=
|
|
566
|
+
const outerLen = items.length;
|
|
567
|
+
for (let i = 0; i < outerLen; i++) totalLen += items[i].length;
|
|
467
568
|
const result = new Array(totalLen);
|
|
468
569
|
let idx = 0;
|
|
469
570
|
for (let i = 0; i < outerLen; i++) {
|
|
470
|
-
const chunk =
|
|
571
|
+
const chunk = items[i];
|
|
471
572
|
const innerLen = chunk.length;
|
|
472
573
|
for (let j = 0; j < innerLen; j++) result[idx++] = chunk[j];
|
|
473
574
|
}
|
|
@@ -476,16 +577,19 @@ const flatten = (data) => {
|
|
|
476
577
|
/**
|
|
477
578
|
* Maps each element to an array and flattens the result.
|
|
478
579
|
*
|
|
580
|
+
* @see {@link map} for mapping without flattening.
|
|
581
|
+
* @see {@link flatten} for flattening without mapping.
|
|
582
|
+
*
|
|
479
583
|
* @example
|
|
480
584
|
* ```ts
|
|
481
585
|
* pipe([1, 2, 3], Arr.flatMap(n => [n, n * 10])); // [1, 10, 2, 20, 3, 30]
|
|
482
586
|
* ```
|
|
483
587
|
*/
|
|
484
|
-
const flatMap = (
|
|
485
|
-
const n =
|
|
588
|
+
const flatMap = (transform) => (items) => {
|
|
589
|
+
const n = items.length;
|
|
486
590
|
const result = [];
|
|
487
591
|
for (let i = 0; i < n; i++) {
|
|
488
|
-
const chunk =
|
|
592
|
+
const chunk = transform(items[i]);
|
|
489
593
|
const m = chunk.length;
|
|
490
594
|
for (let j = 0; j < m; j++) result.push(chunk[j]);
|
|
491
595
|
}
|
|
@@ -494,32 +598,38 @@ const flatMap = (f) => (data) => {
|
|
|
494
598
|
/**
|
|
495
599
|
* Reduces an array from the left.
|
|
496
600
|
*
|
|
601
|
+
* @see {@link scan} for preserving intermediate accumulation states.
|
|
602
|
+
*
|
|
497
603
|
* @example
|
|
498
604
|
* ```ts
|
|
499
605
|
* pipe([1, 2, 3], Arr.reduce(0, (acc, n) => acc + n)); // 6
|
|
500
606
|
* ```
|
|
501
607
|
*/
|
|
502
|
-
const reduce$2 = (initial,
|
|
503
|
-
const _traverseTask = Object.assign((f) => ArrTask.traverse(f), { Result: ArrTaskResult.traverse });
|
|
608
|
+
const reduce$2 = (initial, reducer) => (items) => items.reduce(reducer, initial);
|
|
609
|
+
const _traverseTask = Object.assign((f, options) => ArrTask.traverse(f, options), { Result: ArrTaskResult.traverse });
|
|
504
610
|
const _sequenceTask = Object.assign((data) => ArrTask.sequence(data), { Result: ArrTaskResult.sequence });
|
|
505
611
|
/**
|
|
506
612
|
* Prepends a value to the beginning of an array, returning a NonEmptyArr.
|
|
507
613
|
*
|
|
614
|
+
* @see {@link append} for adding an element to the end.
|
|
615
|
+
*
|
|
508
616
|
* @example
|
|
509
617
|
* ```ts
|
|
510
618
|
* pipe([1, 2], Arr.prepend(0)); // [0, 1, 2]
|
|
511
619
|
* ```
|
|
512
620
|
*/
|
|
513
|
-
const prepend = (
|
|
621
|
+
const prepend = (item) => (items) => [item, ...items];
|
|
514
622
|
/**
|
|
515
623
|
* Appends a value to the end of an array, returning a NonEmptyArr.
|
|
516
624
|
*
|
|
625
|
+
* @see {@link prepend} for adding an element to the beginning.
|
|
626
|
+
*
|
|
517
627
|
* @example
|
|
518
628
|
* ```ts
|
|
519
629
|
* pipe([1, 2], Arr.append(3)); // [1, 2, 3]
|
|
520
630
|
* ```
|
|
521
631
|
*/
|
|
522
|
-
const append = (
|
|
632
|
+
const append = (item) => (items) => [...items, item];
|
|
523
633
|
/**
|
|
524
634
|
* Returns the length of an array.
|
|
525
635
|
*
|
|
@@ -528,31 +638,35 @@ const append = (value) => (data) => [...data, value];
|
|
|
528
638
|
* Arr.size([1, 2, 3]); // 3
|
|
529
639
|
* ```
|
|
530
640
|
*/
|
|
531
|
-
const size$3 = (
|
|
641
|
+
const size$3 = (items) => items.length;
|
|
532
642
|
/**
|
|
533
643
|
* Returns true if any element satisfies the predicate.
|
|
534
644
|
*
|
|
645
|
+
* @see {@link every} for checking whether all elements satisfy a predicate.
|
|
646
|
+
*
|
|
535
647
|
* @example
|
|
536
648
|
* ```ts
|
|
537
649
|
* pipe([1, 2, 3], Arr.some(n => n > 2)); // true
|
|
538
650
|
* ```
|
|
539
651
|
*/
|
|
540
|
-
const some = (predicate) => (
|
|
541
|
-
const n =
|
|
542
|
-
for (let i = 0; i < n; i++) if (predicate(
|
|
652
|
+
const some = (predicate) => (items) => {
|
|
653
|
+
const n = items.length;
|
|
654
|
+
for (let i = 0; i < n; i++) if (predicate(items[i])) return true;
|
|
543
655
|
return false;
|
|
544
656
|
};
|
|
545
657
|
/**
|
|
546
658
|
* Returns true if all elements satisfy the predicate.
|
|
547
659
|
*
|
|
660
|
+
* @see {@link some} for checking whether any element satisfies a predicate.
|
|
661
|
+
*
|
|
548
662
|
* @example
|
|
549
663
|
* ```ts
|
|
550
664
|
* pipe([1, 2, 3], Arr.every(n => n > 0)); // true
|
|
551
665
|
* ```
|
|
552
666
|
*/
|
|
553
|
-
const every = (predicate) => (
|
|
554
|
-
const n =
|
|
555
|
-
for (let i = 0; i < n; i++) if (!predicate(
|
|
667
|
+
const every = (predicate) => (items) => {
|
|
668
|
+
const n = items.length;
|
|
669
|
+
for (let i = 0; i < n; i++) if (!predicate(items[i])) return false;
|
|
556
670
|
return true;
|
|
557
671
|
};
|
|
558
672
|
/**
|
|
@@ -563,11 +677,13 @@ const every = (predicate) => (data) => {
|
|
|
563
677
|
* Arr.reverse([1, 2, 3]); // [3, 2, 1]
|
|
564
678
|
* ```
|
|
565
679
|
*/
|
|
566
|
-
const reverse = (
|
|
680
|
+
const reverse = (items) => [...items].toReversed();
|
|
567
681
|
/**
|
|
568
682
|
* Returns a new array with `item` inserted before the element at `index`.
|
|
569
683
|
* Negative indices are clamped to 0; indices beyond the array length append to the end.
|
|
570
684
|
*
|
|
685
|
+
* @see {@link removeAt} for removing an element at an index.
|
|
686
|
+
*
|
|
571
687
|
* @example
|
|
572
688
|
* ```ts
|
|
573
689
|
* pipe([1, 2, 3], Arr.insertAt(1, 99)); // [1, 99, 2, 3]
|
|
@@ -575,11 +691,11 @@ const reverse = (data) => [...data].toReversed();
|
|
|
575
691
|
* pipe([1, 2, 3], Arr.insertAt(3, 99)); // [1, 2, 3, 99]
|
|
576
692
|
* ```
|
|
577
693
|
*/
|
|
578
|
-
const insertAt = (index, item) => (
|
|
579
|
-
const i = Math.max(0, Math.min(index,
|
|
580
|
-
const arr =
|
|
694
|
+
const insertAt = (index, item) => (items) => {
|
|
695
|
+
const i = Math.max(0, Math.min(index, items.length));
|
|
696
|
+
const arr = items;
|
|
581
697
|
if (typeof arr.toSpliced === "function") return arr.toSpliced(i, 0, item);
|
|
582
|
-
const result = [...
|
|
698
|
+
const result = [...items];
|
|
583
699
|
result.splice(i, 0, item);
|
|
584
700
|
return result;
|
|
585
701
|
};
|
|
@@ -587,6 +703,8 @@ const insertAt = (index, item) => (data) => {
|
|
|
587
703
|
* Returns a new array with the element at `index` removed.
|
|
588
704
|
* Returns the original array unchanged if `index` is out of bounds.
|
|
589
705
|
*
|
|
706
|
+
* @see {@link insertAt} for inserting an element at an index.
|
|
707
|
+
*
|
|
590
708
|
* @example
|
|
591
709
|
* ```ts
|
|
592
710
|
* pipe([1, 2, 3], Arr.removeAt(1)); // [1, 3]
|
|
@@ -594,76 +712,90 @@ const insertAt = (index, item) => (data) => {
|
|
|
594
712
|
* pipe([1, 2, 3], Arr.removeAt(5)); // [1, 2, 3]
|
|
595
713
|
* ```
|
|
596
714
|
*/
|
|
597
|
-
const removeAt = (index) => (
|
|
598
|
-
if (index < 0 || index >=
|
|
599
|
-
const arr =
|
|
715
|
+
const removeAt = (index) => (items) => {
|
|
716
|
+
if (index < 0 || index >= items.length) return items;
|
|
717
|
+
const arr = items;
|
|
600
718
|
if (typeof arr.toSpliced === "function") return arr.toSpliced(index, 1);
|
|
601
|
-
const result = [...
|
|
719
|
+
const result = [...items];
|
|
602
720
|
result.splice(index, 1);
|
|
603
721
|
return result;
|
|
604
722
|
};
|
|
605
723
|
/**
|
|
606
724
|
* Takes the first n elements from an array.
|
|
607
725
|
*
|
|
726
|
+
* @see {@link drop} for discarding the first n elements.
|
|
727
|
+
* @see {@link takeWhile} for taking elements based on a predicate.
|
|
728
|
+
*
|
|
608
729
|
* @example
|
|
609
730
|
* ```ts
|
|
610
731
|
* pipe([1, 2, 3, 4], Arr.take(2)); // [1, 2]
|
|
611
732
|
* ```
|
|
612
733
|
*/
|
|
613
|
-
const take = (
|
|
734
|
+
const take = (count) => (items) => count <= 0 ? [] : items.slice(0, count);
|
|
614
735
|
/**
|
|
615
736
|
* Drops the first n elements from an array.
|
|
616
737
|
*
|
|
738
|
+
* @see {@link take} for keeping the first n elements.
|
|
739
|
+
* @see {@link dropWhile} for discarding elements based on a predicate.
|
|
740
|
+
*
|
|
617
741
|
* @example
|
|
618
742
|
* ```ts
|
|
619
743
|
* pipe([1, 2, 3, 4], Arr.drop(2)); // [3, 4]
|
|
620
744
|
* ```
|
|
621
745
|
*/
|
|
622
|
-
const drop = (
|
|
746
|
+
const drop = (count) => (items) => items.slice(count);
|
|
623
747
|
/**
|
|
624
748
|
* Takes elements from the start while the predicate holds.
|
|
625
749
|
*
|
|
750
|
+
* @see {@link dropWhile} for discarding elements while a predicate holds.
|
|
751
|
+
* @see {@link take} for taking a fixed count of elements.
|
|
752
|
+
*
|
|
626
753
|
* @example
|
|
627
754
|
* ```ts
|
|
628
755
|
* pipe([1, 2, 3, 1], Arr.takeWhile(n => n < 3)); // [1, 2]
|
|
629
756
|
* ```
|
|
630
757
|
*/
|
|
631
|
-
const takeWhile = (predicate) => (
|
|
758
|
+
const takeWhile = (predicate) => (items) => {
|
|
632
759
|
const result = [];
|
|
633
|
-
for (const
|
|
634
|
-
if (!predicate(
|
|
635
|
-
result.push(
|
|
760
|
+
for (const item of items) {
|
|
761
|
+
if (!predicate(item)) break;
|
|
762
|
+
result.push(item);
|
|
636
763
|
}
|
|
637
764
|
return result;
|
|
638
765
|
};
|
|
639
766
|
/**
|
|
640
767
|
* Drops elements from the start while the predicate holds.
|
|
641
768
|
*
|
|
769
|
+
* @see {@link takeWhile} for keeping elements while a predicate holds.
|
|
770
|
+
* @see {@link drop} for discarding a fixed count of elements.
|
|
771
|
+
*
|
|
642
772
|
* @example
|
|
643
773
|
* ```ts
|
|
644
774
|
* pipe([1, 2, 3, 1], Arr.dropWhile(n => n < 3)); // [3, 1]
|
|
645
775
|
* ```
|
|
646
776
|
*/
|
|
647
|
-
const dropWhile = (predicate) => (
|
|
777
|
+
const dropWhile = (predicate) => (items) => {
|
|
648
778
|
let i = 0;
|
|
649
|
-
while (i <
|
|
650
|
-
return
|
|
779
|
+
while (i < items.length && predicate(items[i])) i++;
|
|
780
|
+
return items.slice(i);
|
|
651
781
|
};
|
|
652
782
|
/**
|
|
653
783
|
* Like `reduce`, but returns every intermediate accumulator as an array.
|
|
654
784
|
* The initial value is not included — the output has the same length as the input.
|
|
655
785
|
*
|
|
786
|
+
* @see {@link reduce} for computing only the final accumulator value.
|
|
787
|
+
*
|
|
656
788
|
* @example
|
|
657
789
|
* ```ts
|
|
658
790
|
* pipe([1, 2, 3], Arr.scan(0, (acc, n) => acc + n)); // [1, 3, 6]
|
|
659
791
|
* ```
|
|
660
792
|
*/
|
|
661
|
-
const scan = (initial,
|
|
662
|
-
const n =
|
|
793
|
+
const scan = (initial, reducer) => (items) => {
|
|
794
|
+
const n = items.length;
|
|
663
795
|
const result = new Array(n);
|
|
664
796
|
let acc = initial;
|
|
665
797
|
for (let i = 0; i < n; i++) {
|
|
666
|
-
acc =
|
|
798
|
+
acc = reducer(acc, items[i]);
|
|
667
799
|
result[i] = acc;
|
|
668
800
|
}
|
|
669
801
|
return result;
|
|
@@ -679,28 +811,31 @@ const scan = (initial, f) => (data) => {
|
|
|
679
811
|
* pipe([1, 2, 3], Arr.splitAt(10)); // [[1, 2, 3], []]
|
|
680
812
|
* ```
|
|
681
813
|
*/
|
|
682
|
-
const splitAt = (index) => (
|
|
814
|
+
const splitAt = (index) => (items) => {
|
|
683
815
|
const i = Math.max(0, index);
|
|
684
|
-
return [
|
|
816
|
+
return [items.slice(0, i), items.slice(i)];
|
|
685
817
|
};
|
|
686
818
|
/**
|
|
687
819
|
* Partitions an array by applying a function returning `Maybe<B>`.
|
|
688
820
|
* Elements returning `None` are gathered into `failures` (original `A` values);
|
|
689
821
|
* elements returning `Some(b)` are gathered into `successes` (`B` values).
|
|
690
822
|
*
|
|
823
|
+
* @see {@link partitionMap} for partitioning with a Result mapper.
|
|
824
|
+
* @see {@link partition} for partitioning with a boolean predicate.
|
|
825
|
+
*
|
|
691
826
|
* @example
|
|
692
827
|
* ```ts
|
|
693
828
|
* const parseNumber = (s: string) => isNaN(Number(s)) ? Maybe.make.none() : Maybe.make.some(Number(s));
|
|
694
829
|
* pipe(["1", "abc", "3"], Arr.partitionMaybe(parseNumber)); // [["abc"], [1, 3]]
|
|
695
830
|
* ```
|
|
696
831
|
*/
|
|
697
|
-
const partitionMaybe = (
|
|
832
|
+
const partitionMaybe = (transform) => (items) => {
|
|
698
833
|
const failures = [];
|
|
699
834
|
const successes = [];
|
|
700
|
-
for (let i = 0; i <
|
|
701
|
-
const res =
|
|
835
|
+
for (let i = 0; i < items.length; i++) {
|
|
836
|
+
const res = transform(items[i]);
|
|
702
837
|
if (res.kind === "Some") successes.push(res.value);
|
|
703
|
-
else failures.push(
|
|
838
|
+
else failures.push(items[i]);
|
|
704
839
|
}
|
|
705
840
|
return [failures, successes];
|
|
706
841
|
};
|
|
@@ -715,13 +850,15 @@ const partitionMaybe = (f) => (data) => {
|
|
|
715
850
|
* pipe([10, 20, 30], Arr.at(5)); // None
|
|
716
851
|
* ```
|
|
717
852
|
*/
|
|
718
|
-
const at = (index) => (
|
|
719
|
-
const targetIndex = index < 0 ?
|
|
720
|
-
if (targetIndex < 0 || targetIndex >=
|
|
721
|
-
return Maybe.make.some(
|
|
853
|
+
const at = (index) => (items) => {
|
|
854
|
+
const targetIndex = index < 0 ? items.length + index : index;
|
|
855
|
+
if (targetIndex < 0 || targetIndex >= items.length) return Maybe.make.none();
|
|
856
|
+
return Maybe.make.some(items[targetIndex]);
|
|
722
857
|
};
|
|
723
858
|
/**
|
|
724
|
-
* Finds the first element in an array for which `
|
|
859
|
+
* Finds the first element in an array for which `transform` returns `Some(b)`.
|
|
860
|
+
*
|
|
861
|
+
* @see {@link findFirst} for finding elements with a boolean predicate.
|
|
725
862
|
*
|
|
726
863
|
* @example
|
|
727
864
|
* ```ts
|
|
@@ -731,9 +868,9 @@ const at = (index) => (data) => {
|
|
|
731
868
|
* ); // Some(1)
|
|
732
869
|
* ```
|
|
733
870
|
*/
|
|
734
|
-
const findMap = (
|
|
735
|
-
for (let i = 0; i <
|
|
736
|
-
const res =
|
|
871
|
+
const findMap = (transform) => (items) => {
|
|
872
|
+
for (let i = 0; i < items.length; i++) {
|
|
873
|
+
const res = transform(items[i]);
|
|
737
874
|
if (res.kind === "Some") return res;
|
|
738
875
|
}
|
|
739
876
|
return Maybe.make.none();
|
|
@@ -741,6 +878,8 @@ const findMap = (f) => (data) => {
|
|
|
741
878
|
/**
|
|
742
879
|
* Indexes elements of an array into a `ReadonlyMap<K, A>` using a key extraction function.
|
|
743
880
|
*
|
|
881
|
+
* @see {@link groupBy} for grouping multiple items per key into arrays.
|
|
882
|
+
*
|
|
744
883
|
* @example
|
|
745
884
|
* ```ts
|
|
746
885
|
* pipe(
|
|
@@ -749,9 +888,9 @@ const findMap = (f) => (data) => {
|
|
|
749
888
|
* ); // ReadonlyMap { 1 => { id: 1, name: "Alice" }, 2 => { id: 2, name: "Bob" } }
|
|
750
889
|
* ```
|
|
751
890
|
*/
|
|
752
|
-
const indexBy = (
|
|
891
|
+
const indexBy = (keySelector) => (items) => {
|
|
753
892
|
const resultMap = new globalThis.Map();
|
|
754
|
-
for (let i = 0; i <
|
|
893
|
+
for (let i = 0; i < items.length; i++) resultMap.set(keySelector(items[i]), items[i]);
|
|
755
894
|
return resultMap;
|
|
756
895
|
};
|
|
757
896
|
/**
|
|
@@ -763,16 +902,18 @@ const indexBy = (keyFn) => (data) => {
|
|
|
763
902
|
* // ReadonlyMap { "a" => 3, "b" => 2, "c" => 1 }
|
|
764
903
|
* ```
|
|
765
904
|
*/
|
|
766
|
-
const frequencies = (
|
|
905
|
+
const frequencies = (items) => {
|
|
767
906
|
const resultMap = new globalThis.Map();
|
|
768
|
-
for (let i = 0; i <
|
|
769
|
-
const item =
|
|
907
|
+
for (let i = 0; i < items.length; i++) {
|
|
908
|
+
const item = items[i];
|
|
770
909
|
resultMap.set(item, (resultMap.get(item) ?? 0) + 1);
|
|
771
910
|
}
|
|
772
911
|
return resultMap;
|
|
773
912
|
};
|
|
774
913
|
/**
|
|
775
|
-
* Groups consecutive elements that share the same key returned by `
|
|
914
|
+
* Groups consecutive elements that share the same key returned by `keySelector`.
|
|
915
|
+
*
|
|
916
|
+
* @see {@link chunksOf} for fixed-size chunking.
|
|
776
917
|
*
|
|
777
918
|
* @example
|
|
778
919
|
* ```ts
|
|
@@ -782,14 +923,14 @@ const frequencies = (data) => {
|
|
|
782
923
|
* ); // [[1, 1], [2], [3, 3], [1]]
|
|
783
924
|
* ```
|
|
784
925
|
*/
|
|
785
|
-
const chunkBy = (
|
|
786
|
-
if (
|
|
926
|
+
const chunkBy = (keySelector) => (items) => {
|
|
927
|
+
if (items.length === 0) return [];
|
|
787
928
|
const result = [];
|
|
788
|
-
let currentChunk = [
|
|
789
|
-
let currentKey =
|
|
790
|
-
for (let i = 1; i <
|
|
791
|
-
const item =
|
|
792
|
-
const key =
|
|
929
|
+
let currentChunk = [items[0]];
|
|
930
|
+
let currentKey = keySelector(items[0]);
|
|
931
|
+
for (let i = 1; i < items.length; i++) {
|
|
932
|
+
const item = items[i];
|
|
933
|
+
const key = keySelector(item);
|
|
793
934
|
if (Object.is(key, currentKey)) currentChunk.push(item);
|
|
794
935
|
else {
|
|
795
936
|
result.push(currentChunk);
|
|
@@ -804,20 +945,22 @@ const chunkBy = (keyFn) => (data) => {
|
|
|
804
945
|
* Removes consecutive duplicate elements.
|
|
805
946
|
* An optional `Equality<A>` can be provided (defaults to `Object.is`).
|
|
806
947
|
*
|
|
948
|
+
* @see {@link uniq} for deduplicating across the entire array.
|
|
949
|
+
*
|
|
807
950
|
* @example
|
|
808
951
|
* ```ts
|
|
809
952
|
* Arr.dedupeAdjacent()([1, 1, 2, 2, 1, 3]); // [1, 2, 1, 3]
|
|
810
953
|
* ```
|
|
811
954
|
*/
|
|
812
|
-
const dedupeAdjacent = (
|
|
813
|
-
if (
|
|
814
|
-
const result = [
|
|
815
|
-
for (let i = 1; i <
|
|
955
|
+
const dedupeAdjacent = (areEqual = (first, second) => Object.is(first, second)) => (items) => {
|
|
956
|
+
if (items.length === 0) return [];
|
|
957
|
+
const result = [items[0]];
|
|
958
|
+
for (let i = 1; i < items.length; i++) if (!areEqual(items[i], result[result.length - 1])) result.push(items[i]);
|
|
816
959
|
return result;
|
|
817
960
|
};
|
|
818
961
|
/**
|
|
819
962
|
* Produces a sliding window of `size` elements over an array, advancing by `step` (default `1`).
|
|
820
|
-
* Returns an empty array if `size <= 0` or `size >
|
|
963
|
+
* Returns an empty array if `size <= 0` or `size > items.length`.
|
|
821
964
|
*
|
|
822
965
|
* @example
|
|
823
966
|
* ```ts
|
|
@@ -825,15 +968,15 @@ const dedupeAdjacent = (eq = (a, b) => Object.is(a, b)) => (data) => {
|
|
|
825
968
|
* pipe([1, 2, 3, 4], Arr.windowed(2, { step: 2 })); // [[1, 2], [3, 4]]
|
|
826
969
|
* ```
|
|
827
970
|
*/
|
|
828
|
-
const windowed = (windowSize, options) => (
|
|
971
|
+
const windowed = (windowSize, options) => (items) => {
|
|
829
972
|
const step = options?.step ?? 1;
|
|
830
|
-
if (windowSize <= 0 || step <= 0 ||
|
|
973
|
+
if (windowSize <= 0 || step <= 0 || items.length < windowSize) return [];
|
|
831
974
|
const result = [];
|
|
832
|
-
for (let i = 0; i <=
|
|
975
|
+
for (let i = 0; i <= items.length - windowSize; i += step) result.push(items.slice(i, i + windowSize));
|
|
833
976
|
return result;
|
|
834
977
|
};
|
|
835
978
|
/**
|
|
836
|
-
* Generates an array from an initial seed state until `
|
|
979
|
+
* Generates an array from an initial seed state until `step` returns `None`.
|
|
837
980
|
*
|
|
838
981
|
* @example
|
|
839
982
|
* ```ts
|
|
@@ -841,11 +984,11 @@ const windowed = (windowSize, options) => (data) => {
|
|
|
841
984
|
* // [1, 2, 3]
|
|
842
985
|
* ```
|
|
843
986
|
*/
|
|
844
|
-
const unfold = (initial,
|
|
987
|
+
const unfold = (initial, step) => {
|
|
845
988
|
const result = [];
|
|
846
989
|
let currentState = initial;
|
|
847
990
|
while (true) {
|
|
848
|
-
const next =
|
|
991
|
+
const next = step(currentState);
|
|
849
992
|
if (next.kind === "None") break;
|
|
850
993
|
const [item, nextState] = next.value;
|
|
851
994
|
result.push(item);
|
|
@@ -886,6 +1029,7 @@ const Arr = {
|
|
|
886
1029
|
prepend,
|
|
887
1030
|
append,
|
|
888
1031
|
size: size$3,
|
|
1032
|
+
length: size$3,
|
|
889
1033
|
some,
|
|
890
1034
|
every,
|
|
891
1035
|
reverse,
|
|
@@ -906,33 +1050,44 @@ const Arr = {
|
|
|
906
1050
|
dedupeAdjacent,
|
|
907
1051
|
windowed,
|
|
908
1052
|
unfold,
|
|
909
|
-
from: { Array: (data) => data.length > 0 ? Maybe.make.some(data) : Maybe.make.none() },
|
|
910
1053
|
is: {
|
|
911
|
-
|
|
912
|
-
|
|
1054
|
+
/**
|
|
1055
|
+
* Returns `true` when the array is empty.
|
|
1056
|
+
*
|
|
1057
|
+
* @see {@link nonEmpty} for checking if an array contains elements.
|
|
1058
|
+
*/
|
|
1059
|
+
empty: (items) => items.length === 0,
|
|
1060
|
+
/**
|
|
1061
|
+
* Returns `true` when the array contains at least one element.
|
|
1062
|
+
*
|
|
1063
|
+
* @see {@link empty} for checking if an array is empty.
|
|
1064
|
+
*/
|
|
1065
|
+
nonEmpty: (items) => isNonEmptyArr(items)
|
|
913
1066
|
},
|
|
914
1067
|
traverse: {
|
|
915
1068
|
Maybe: ArrMaybe.traverse,
|
|
916
1069
|
Result: ArrResult.traverse,
|
|
917
|
-
Task: _traverseTask
|
|
1070
|
+
Task: _traverseTask,
|
|
1071
|
+
Validation: ArrValidation.traverse
|
|
918
1072
|
},
|
|
919
1073
|
sequence: {
|
|
920
1074
|
Maybe: ArrMaybe.sequence,
|
|
921
1075
|
Result: ArrResult.sequence,
|
|
922
|
-
Task: _sequenceTask
|
|
1076
|
+
Task: _sequenceTask,
|
|
1077
|
+
Validation: ArrValidation.sequence
|
|
923
1078
|
},
|
|
924
1079
|
NonEmpty: {
|
|
925
|
-
singleton: (
|
|
926
|
-
from: {
|
|
927
|
-
head: (
|
|
928
|
-
last: (
|
|
929
|
-
tail: (
|
|
930
|
-
reduce: (
|
|
931
|
-
map: (
|
|
932
|
-
mapWithIndex: (
|
|
933
|
-
intersperse: (
|
|
934
|
-
concat: (other) => (
|
|
935
|
-
reverse: (
|
|
1080
|
+
singleton: (item) => [item],
|
|
1081
|
+
from: { array: (items) => isNonEmptyArr(items) ? Maybe.make.some(items) : Maybe.make.none() },
|
|
1082
|
+
head: (items) => items[0],
|
|
1083
|
+
last: (items) => items[items.length - 1],
|
|
1084
|
+
tail: (items) => items.slice(1),
|
|
1085
|
+
reduce: (reducer) => (items) => items.reduce(reducer),
|
|
1086
|
+
map: (transform) => (items) => map$3(transform)(items),
|
|
1087
|
+
mapWithIndex: (transform) => (items) => mapWithIndex(transform)(items),
|
|
1088
|
+
intersperse: (separator) => (items) => intersperse(separator)(items),
|
|
1089
|
+
concat: (other) => (items) => concat(other)(items),
|
|
1090
|
+
reverse: (items) => reverse(items)
|
|
936
1091
|
}
|
|
937
1092
|
};
|
|
938
1093
|
//#endregion
|
|
@@ -963,30 +1118,36 @@ const BigNum = {
|
|
|
963
1118
|
* BigNum.is.zero(5n); // false
|
|
964
1119
|
* ```
|
|
965
1120
|
*/
|
|
966
|
-
zero: (
|
|
1121
|
+
zero: (value) => value === 0n,
|
|
967
1122
|
/**
|
|
968
1123
|
* Returns `true` when the bigint is an even integer.
|
|
969
1124
|
*
|
|
1125
|
+
* @see {@link BigNum.is.odd} to check if a bigint is odd.
|
|
1126
|
+
*
|
|
970
1127
|
* @example
|
|
971
1128
|
* ```ts
|
|
972
1129
|
* BigNum.is.even(4n); // true
|
|
973
1130
|
* BigNum.is.even(3n); // false
|
|
974
1131
|
* ```
|
|
975
1132
|
*/
|
|
976
|
-
even: (
|
|
1133
|
+
even: (value) => value % 2n === 0n,
|
|
977
1134
|
/**
|
|
978
1135
|
* Returns `true` when the bigint is an odd integer.
|
|
979
1136
|
*
|
|
1137
|
+
* @see {@link BigNum.is.even} to check if a bigint is even.
|
|
1138
|
+
*
|
|
980
1139
|
* @example
|
|
981
1140
|
* ```ts
|
|
982
1141
|
* BigNum.is.odd(3n); // true
|
|
983
1142
|
* BigNum.is.odd(4n); // false
|
|
984
1143
|
* ```
|
|
985
1144
|
*/
|
|
986
|
-
odd: (
|
|
1145
|
+
odd: (value) => value % 2n !== 0n,
|
|
987
1146
|
/**
|
|
988
1147
|
* Returns `true` when the bigint is strictly greater than zero (`0n`).
|
|
989
1148
|
*
|
|
1149
|
+
* @see {@link BigNum.is.negative} to check if a bigint is less than zero.
|
|
1150
|
+
*
|
|
990
1151
|
* @example
|
|
991
1152
|
* ```ts
|
|
992
1153
|
* BigNum.is.positive(5n); // true
|
|
@@ -994,10 +1155,12 @@ const BigNum = {
|
|
|
994
1155
|
* BigNum.is.positive(-5n); // false
|
|
995
1156
|
* ```
|
|
996
1157
|
*/
|
|
997
|
-
positive: (
|
|
1158
|
+
positive: (value) => value > 0n,
|
|
998
1159
|
/**
|
|
999
1160
|
* Returns `true` when the bigint is strictly less than zero (`0n`).
|
|
1000
1161
|
*
|
|
1162
|
+
* @see {@link BigNum.is.positive} to check if a bigint is greater than zero.
|
|
1163
|
+
*
|
|
1001
1164
|
* @example
|
|
1002
1165
|
* ```ts
|
|
1003
1166
|
* BigNum.is.negative(-5n); // true
|
|
@@ -1005,7 +1168,7 @@ const BigNum = {
|
|
|
1005
1168
|
* BigNum.is.negative(5n); // false
|
|
1006
1169
|
* ```
|
|
1007
1170
|
*/
|
|
1008
|
-
negative: (
|
|
1171
|
+
negative: (value) => value < 0n
|
|
1009
1172
|
},
|
|
1010
1173
|
from: {
|
|
1011
1174
|
/**
|
|
@@ -1017,10 +1180,10 @@ const BigNum = {
|
|
|
1017
1180
|
* BigNum.from.string("abc"); // None
|
|
1018
1181
|
* ```
|
|
1019
1182
|
*/
|
|
1020
|
-
string: (
|
|
1183
|
+
string: (text) => {
|
|
1021
1184
|
try {
|
|
1022
|
-
if (
|
|
1023
|
-
return Maybe.make.some(BigInt(
|
|
1185
|
+
if (text.trim() === "") return Maybe.make.none();
|
|
1186
|
+
return Maybe.make.some(BigInt(text));
|
|
1024
1187
|
} catch {
|
|
1025
1188
|
return Maybe.make.none();
|
|
1026
1189
|
}
|
|
@@ -1034,9 +1197,9 @@ const BigNum = {
|
|
|
1034
1197
|
* BigNum.from.number(3.14); // None
|
|
1035
1198
|
* ```
|
|
1036
1199
|
*/
|
|
1037
|
-
number: (
|
|
1038
|
-
if (!Number.isInteger(
|
|
1039
|
-
return Maybe.make.some(BigInt(
|
|
1200
|
+
number: (value) => {
|
|
1201
|
+
if (!Number.isInteger(value) || value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) return Maybe.make.none();
|
|
1202
|
+
return Maybe.make.some(BigInt(value));
|
|
1040
1203
|
}
|
|
1041
1204
|
},
|
|
1042
1205
|
to: {
|
|
@@ -1049,75 +1212,100 @@ const BigNum = {
|
|
|
1049
1212
|
* BigNum.to.number(9007199254740993n); // None
|
|
1050
1213
|
* ```
|
|
1051
1214
|
*/
|
|
1052
|
-
number: (
|
|
1053
|
-
if (
|
|
1054
|
-
return Maybe.make.some(Number(
|
|
1215
|
+
number: (value) => {
|
|
1216
|
+
if (value < BigInt(Number.MIN_SAFE_INTEGER) || value > BigInt(Number.MAX_SAFE_INTEGER)) return Maybe.make.none();
|
|
1217
|
+
return Maybe.make.some(Number(value));
|
|
1055
1218
|
} },
|
|
1056
1219
|
/**
|
|
1057
|
-
* Adds `
|
|
1220
|
+
* Adds `amount` to `value`. Data-last curried signature: `add(amount)(value)` = `value + amount`.
|
|
1221
|
+
*
|
|
1222
|
+
* @see {@link BigNum.subtract} to subtract an amount from a bigint.
|
|
1058
1223
|
*
|
|
1059
1224
|
* @example
|
|
1060
1225
|
* ```ts
|
|
1061
1226
|
* pipe(10n, BigNum.add(5n)); // 15n
|
|
1062
1227
|
* ```
|
|
1063
1228
|
*/
|
|
1064
|
-
add: (
|
|
1229
|
+
add: (amount) => (value) => value + amount,
|
|
1065
1230
|
/**
|
|
1066
|
-
* Subtracts `
|
|
1231
|
+
* Subtracts `amount` from `from`. Data-last curried signature: `subtract(amount)(from)` = `from - amount`.
|
|
1232
|
+
*
|
|
1233
|
+
* @see {@link BigNum.add} to add an amount to a bigint.
|
|
1067
1234
|
*
|
|
1068
1235
|
* @example
|
|
1069
1236
|
* ```ts
|
|
1070
|
-
* pipe(10n, BigNum.
|
|
1237
|
+
* pipe(10n, BigNum.subtract(3n)); // 7n
|
|
1071
1238
|
* ```
|
|
1072
1239
|
*/
|
|
1073
|
-
|
|
1240
|
+
subtract: (amount) => (from) => from - amount,
|
|
1074
1241
|
/**
|
|
1075
|
-
* Multiplies `
|
|
1242
|
+
* Multiplies `value` by `factor`. Data-last curried signature: `multiply(factor)(value)` = `value * factor`.
|
|
1243
|
+
*
|
|
1244
|
+
* @see {@link BigNum.divide} to divide a bigint by a divisor.
|
|
1076
1245
|
*
|
|
1077
1246
|
* @example
|
|
1078
1247
|
* ```ts
|
|
1079
|
-
* pipe(6n, BigNum.
|
|
1248
|
+
* pipe(6n, BigNum.multiply(7n)); // 42n
|
|
1080
1249
|
* ```
|
|
1081
1250
|
*/
|
|
1082
|
-
|
|
1251
|
+
multiply: (factor) => (value) => value * factor,
|
|
1083
1252
|
/**
|
|
1084
|
-
* Divides `
|
|
1253
|
+
* Divides `dividend` by `divisor`. Returns `None` if `divisor` is `0n`.
|
|
1254
|
+
*
|
|
1255
|
+
* @see {@link BigNum.multiply} to multiply a bigint by a factor.
|
|
1256
|
+
* @see {@link BigNum.remainder} to compute the division remainder.
|
|
1085
1257
|
*
|
|
1086
1258
|
* @example
|
|
1087
1259
|
* ```ts
|
|
1088
|
-
* pipe(20n, BigNum.
|
|
1089
|
-
* pipe(5n, BigNum.
|
|
1260
|
+
* pipe(20n, BigNum.divide(4n)); // Some(5n)
|
|
1261
|
+
* pipe(5n, BigNum.divide(0n)); // None
|
|
1090
1262
|
* ```
|
|
1091
1263
|
*/
|
|
1092
|
-
|
|
1264
|
+
divide: (divisor) => (dividend) => divisor === 0n ? Maybe.make.none() : Maybe.make.some(dividend / divisor),
|
|
1093
1265
|
/**
|
|
1094
|
-
* Computes remainder of `
|
|
1266
|
+
* Computes remainder of `dividend / divisor`. Returns `None` if `divisor` is `0n`.
|
|
1267
|
+
*
|
|
1268
|
+
* @see {@link BigNum.divide} for full division.
|
|
1095
1269
|
*
|
|
1096
1270
|
* @example
|
|
1097
1271
|
* ```ts
|
|
1098
|
-
* pipe(10n, BigNum.
|
|
1099
|
-
* pipe(5n, BigNum.
|
|
1272
|
+
* pipe(10n, BigNum.remainder(3n)); // Some(1n)
|
|
1273
|
+
* pipe(5n, BigNum.remainder(0n)); // None
|
|
1100
1274
|
* ```
|
|
1101
1275
|
*/
|
|
1102
|
-
|
|
1276
|
+
remainder: (divisor) => (dividend) => divisor === 0n ? Maybe.make.none() : Maybe.make.some(dividend % divisor),
|
|
1103
1277
|
/**
|
|
1104
|
-
* Clamps `
|
|
1278
|
+
* Clamps `value` between `min` and `max` (inclusive).
|
|
1105
1279
|
*
|
|
1106
1280
|
* @example
|
|
1107
1281
|
* ```ts
|
|
1108
1282
|
* pipe(150n, BigNum.clamp(0n, 100n)); // 100n
|
|
1109
1283
|
* ```
|
|
1110
1284
|
*/
|
|
1111
|
-
clamp: (min, max) => (
|
|
1285
|
+
clamp: (min, max) => (value) => value < min ? min : value > max ? max : value,
|
|
1286
|
+
/**
|
|
1287
|
+
* Returns `true` when the bigint is between `min` and `max` (both inclusive).
|
|
1288
|
+
*
|
|
1289
|
+
* @see {@link BigNum.inRange} for half-open range checking [start, end).
|
|
1290
|
+
*
|
|
1291
|
+
* @example
|
|
1292
|
+
* ```ts
|
|
1293
|
+
* pipe(5n, BigNum.between(1n, 10n)); // true
|
|
1294
|
+
* pipe(0n, BigNum.between(1n, 10n)); // false
|
|
1295
|
+
* ```
|
|
1296
|
+
*/
|
|
1297
|
+
between: (min, max) => (value) => value >= min && value <= max,
|
|
1112
1298
|
/**
|
|
1113
|
-
* Returns `true` if `
|
|
1299
|
+
* Returns `true` if `value` is in the range `[start, end)` (inclusive start, exclusive end).
|
|
1300
|
+
*
|
|
1301
|
+
* @see {@link BigNum.between} for fully closed range checking [min, max].
|
|
1114
1302
|
*
|
|
1115
1303
|
* @example
|
|
1116
1304
|
* ```ts
|
|
1117
1305
|
* pipe(5n, BigNum.inRange(1n, 10n)); // true
|
|
1118
1306
|
* ```
|
|
1119
1307
|
*/
|
|
1120
|
-
inRange: (start, end) => (
|
|
1308
|
+
inRange: (start, end) => (value) => value >= start && value < end,
|
|
1121
1309
|
/**
|
|
1122
1310
|
* Returns absolute value of a `bigint`.
|
|
1123
1311
|
*
|
|
@@ -1126,65 +1314,69 @@ number: (b) => {
|
|
|
1126
1314
|
* BigNum.abs(-42n); // 42n
|
|
1127
1315
|
* ```
|
|
1128
1316
|
*/
|
|
1129
|
-
abs: (
|
|
1317
|
+
abs: (value) => value < 0n ? -value : value,
|
|
1130
1318
|
/**
|
|
1131
|
-
* Returns the minimum of `
|
|
1319
|
+
* Returns the minimum of `value` and `other`.
|
|
1320
|
+
*
|
|
1321
|
+
* @see {@link BigNum.max} to determine the maximum value.
|
|
1132
1322
|
*
|
|
1133
1323
|
* @example
|
|
1134
1324
|
* ```ts
|
|
1135
1325
|
* pipe(10n, BigNum.min(5n)); // 5n
|
|
1136
1326
|
* ```
|
|
1137
1327
|
*/
|
|
1138
|
-
min: (
|
|
1328
|
+
min: (other) => (value) => value < other ? value : other,
|
|
1139
1329
|
/**
|
|
1140
|
-
* Returns the maximum of `
|
|
1330
|
+
* Returns the maximum of `value` and `other`.
|
|
1331
|
+
*
|
|
1332
|
+
* @see {@link BigNum.min} to determine the minimum value.
|
|
1141
1333
|
*
|
|
1142
1334
|
* @example
|
|
1143
1335
|
* ```ts
|
|
1144
1336
|
* pipe(10n, BigNum.max(5n)); // 10n
|
|
1145
1337
|
* ```
|
|
1146
1338
|
*/
|
|
1147
|
-
max: (
|
|
1339
|
+
max: (other) => (value) => value > other ? value : other
|
|
1148
1340
|
};
|
|
1149
1341
|
//#endregion
|
|
1150
1342
|
//#region src/Data/Bool.ts
|
|
1151
|
-
const isBoolean = (
|
|
1152
|
-
const isTrue = (
|
|
1153
|
-
const isFalse = (
|
|
1154
|
-
const isTruthy = (
|
|
1155
|
-
const isFalsy = (
|
|
1156
|
-
const not = (
|
|
1157
|
-
const and = (that) => (
|
|
1158
|
-
const or = (that) => (
|
|
1159
|
-
const xor = (that) => (
|
|
1160
|
-
const andLazy = (that) => (
|
|
1161
|
-
const orLazy = (that) => (
|
|
1162
|
-
const all = (
|
|
1163
|
-
for (let i = 0; i <
|
|
1343
|
+
const isBoolean = (value) => typeof value === "boolean";
|
|
1344
|
+
const isTrue = (value) => value === true;
|
|
1345
|
+
const isFalse = (value) => value === false;
|
|
1346
|
+
const isTruthy = (value) => Boolean(value);
|
|
1347
|
+
const isFalsy = (value) => !value;
|
|
1348
|
+
const not = (condition) => !condition;
|
|
1349
|
+
const and = (that) => (condition) => condition && that;
|
|
1350
|
+
const or = (that) => (condition) => condition || that;
|
|
1351
|
+
const xor = (that) => (condition) => condition !== that;
|
|
1352
|
+
const andLazy = (that) => (condition) => condition && that();
|
|
1353
|
+
const orLazy = (that) => (condition) => condition || that();
|
|
1354
|
+
const all = (conditions) => {
|
|
1355
|
+
for (let i = 0; i < conditions.length; i++) if (!conditions[i]) return false;
|
|
1164
1356
|
return true;
|
|
1165
1357
|
};
|
|
1166
|
-
const any = (
|
|
1167
|
-
for (let i = 0; i <
|
|
1358
|
+
const any = (conditions) => {
|
|
1359
|
+
for (let i = 0; i < conditions.length; i++) if (conditions[i]) return true;
|
|
1168
1360
|
return false;
|
|
1169
1361
|
};
|
|
1170
|
-
const fold = (onFalse, onTrue) => (
|
|
1171
|
-
const match = (cases) => (
|
|
1172
|
-
const fromString = (
|
|
1173
|
-
const trimmed =
|
|
1362
|
+
const fold = (onFalse, onTrue) => (condition) => condition ? onTrue() : onFalse();
|
|
1363
|
+
const match = (cases) => (condition) => condition ? cases.true() : cases.false();
|
|
1364
|
+
const fromString = (text) => {
|
|
1365
|
+
const trimmed = text.trim().toLowerCase();
|
|
1174
1366
|
if (trimmed === "true") return Maybe.make.some(true);
|
|
1175
1367
|
if (trimmed === "false") return Maybe.make.some(false);
|
|
1176
1368
|
return Maybe.make.none();
|
|
1177
1369
|
};
|
|
1178
|
-
const fromNumber = (
|
|
1179
|
-
if (
|
|
1180
|
-
if (
|
|
1370
|
+
const fromNumber = (value) => {
|
|
1371
|
+
if (value === 1) return Maybe.make.some(true);
|
|
1372
|
+
if (value === 0) return Maybe.make.some(false);
|
|
1181
1373
|
return Maybe.make.none();
|
|
1182
1374
|
};
|
|
1183
1375
|
const fromTruthy = (value) => Boolean(value);
|
|
1184
|
-
const toMaybe = (onTrue) => (
|
|
1185
|
-
const toResult = (onErr, onOk) => (
|
|
1186
|
-
const toNumber = (
|
|
1187
|
-
const toString = (
|
|
1376
|
+
const toMaybe = (onTrue) => (condition) => condition ? Maybe.make.some(onTrue()) : Maybe.make.none();
|
|
1377
|
+
const toResult = (onErr, onOk) => (condition) => condition ? Result.make.ok(onOk()) : Result.make.err(onErr());
|
|
1378
|
+
const toNumber = (condition) => condition ? 1 : 0;
|
|
1379
|
+
const toString = (condition) => condition ? "true" : "false";
|
|
1188
1380
|
const Bool = {
|
|
1189
1381
|
is: {
|
|
1190
1382
|
/**
|
|
@@ -1202,6 +1394,8 @@ const Bool = {
|
|
|
1202
1394
|
/**
|
|
1203
1395
|
* Narrowing guard — checks if a value is strictly `true`.
|
|
1204
1396
|
*
|
|
1397
|
+
* @see {@link Bool.is.false} to check if a value is strictly false.
|
|
1398
|
+
*
|
|
1205
1399
|
* @example
|
|
1206
1400
|
* ```ts
|
|
1207
1401
|
* Bool.is.true(true); // true
|
|
@@ -1212,6 +1406,8 @@ const Bool = {
|
|
|
1212
1406
|
/**
|
|
1213
1407
|
* Narrowing guard — checks if a value is strictly `false`.
|
|
1214
1408
|
*
|
|
1409
|
+
* @see {@link Bool.is.true} to check if a value is strictly true.
|
|
1410
|
+
*
|
|
1215
1411
|
* @example
|
|
1216
1412
|
* ```ts
|
|
1217
1413
|
* Bool.is.false(false); // true
|
|
@@ -1222,6 +1418,8 @@ const Bool = {
|
|
|
1222
1418
|
/**
|
|
1223
1419
|
* Type guard — checks if a value is truthy (not `false`, `0`, `0n`, `""`, `null`, `undefined`, or `NaN`).
|
|
1224
1420
|
*
|
|
1421
|
+
* @see {@link Bool.is.falsy} to check if a value is falsy.
|
|
1422
|
+
*
|
|
1225
1423
|
* @example
|
|
1226
1424
|
* ```ts
|
|
1227
1425
|
* Bool.is.truthy("hello"); // true
|
|
@@ -1234,6 +1432,8 @@ const Bool = {
|
|
|
1234
1432
|
/**
|
|
1235
1433
|
* Type guard — checks if a value is falsy (`false`, `0`, `0n`, `""`, `null`, `undefined`, or `NaN`).
|
|
1236
1434
|
*
|
|
1435
|
+
* @see {@link Bool.is.truthy} to check if a value is truthy.
|
|
1436
|
+
*
|
|
1237
1437
|
* @example
|
|
1238
1438
|
* ```ts
|
|
1239
1439
|
* Bool.is.falsy(""); // true
|
|
@@ -1254,9 +1454,12 @@ const Bool = {
|
|
|
1254
1454
|
*/
|
|
1255
1455
|
not,
|
|
1256
1456
|
/**
|
|
1257
|
-
* Logical AND combinator. Returns `true` only if both `
|
|
1457
|
+
* Logical AND combinator. Returns `true` only if both `condition` and `that` are `true`.
|
|
1258
1458
|
*
|
|
1259
|
-
* Data-last: `pipe(
|
|
1459
|
+
* Data-last: `pipe(condition, Bool.and(that))`.
|
|
1460
|
+
*
|
|
1461
|
+
* @see {@link Bool.or} for logical OR combinator.
|
|
1462
|
+
* @see {@link Bool.andLazy} for short-circuiting lazy evaluation.
|
|
1260
1463
|
*
|
|
1261
1464
|
* @example
|
|
1262
1465
|
* ```ts
|
|
@@ -1266,9 +1469,12 @@ const Bool = {
|
|
|
1266
1469
|
*/
|
|
1267
1470
|
and,
|
|
1268
1471
|
/**
|
|
1269
|
-
* Logical OR combinator. Returns `true` if either `
|
|
1472
|
+
* Logical OR combinator. Returns `true` if either `condition` or `that` is `true`.
|
|
1473
|
+
*
|
|
1474
|
+
* Data-last: `pipe(condition, Bool.or(that))`.
|
|
1270
1475
|
*
|
|
1271
|
-
*
|
|
1476
|
+
* @see {@link Bool.and} for logical AND combinator.
|
|
1477
|
+
* @see {@link Bool.orLazy} for short-circuiting lazy evaluation.
|
|
1272
1478
|
*
|
|
1273
1479
|
* @example
|
|
1274
1480
|
* ```ts
|
|
@@ -1278,9 +1484,9 @@ const Bool = {
|
|
|
1278
1484
|
*/
|
|
1279
1485
|
or,
|
|
1280
1486
|
/**
|
|
1281
|
-
* Logical XOR (exclusive OR) combinator. Returns `true` if exactly one of `
|
|
1487
|
+
* Logical XOR (exclusive OR) combinator. Returns `true` if exactly one of `condition` and `that` is `true`.
|
|
1282
1488
|
*
|
|
1283
|
-
* Data-last: `pipe(
|
|
1489
|
+
* Data-last: `pipe(condition, Bool.xor(that))`.
|
|
1284
1490
|
*
|
|
1285
1491
|
* @example
|
|
1286
1492
|
* ```ts
|
|
@@ -1291,9 +1497,12 @@ const Bool = {
|
|
|
1291
1497
|
xor,
|
|
1292
1498
|
/**
|
|
1293
1499
|
* Lazy logical AND combinator.
|
|
1294
|
-
* If `
|
|
1500
|
+
* If `condition` is `false`, the `that` computation is never evaluated.
|
|
1295
1501
|
*
|
|
1296
|
-
* Data-last: `pipe(
|
|
1502
|
+
* Data-last: `pipe(condition, Bool.andLazy(that))`.
|
|
1503
|
+
*
|
|
1504
|
+
* @see {@link Bool.orLazy} for lazy logical OR combinator.
|
|
1505
|
+
* @see {@link Bool.and} for strict logical AND evaluation.
|
|
1297
1506
|
*
|
|
1298
1507
|
* @example
|
|
1299
1508
|
* ```ts
|
|
@@ -1306,9 +1515,12 @@ const Bool = {
|
|
|
1306
1515
|
andLazy,
|
|
1307
1516
|
/**
|
|
1308
1517
|
* Lazy logical OR combinator.
|
|
1309
|
-
* If `
|
|
1518
|
+
* If `condition` is `true`, the `that` computation is never evaluated.
|
|
1519
|
+
*
|
|
1520
|
+
* Data-last: `pipe(condition, Bool.orLazy(that))`.
|
|
1310
1521
|
*
|
|
1311
|
-
*
|
|
1522
|
+
* @see {@link Bool.andLazy} for lazy logical AND combinator.
|
|
1523
|
+
* @see {@link Bool.or} for strict logical OR evaluation.
|
|
1312
1524
|
*
|
|
1313
1525
|
* @example
|
|
1314
1526
|
* ```ts
|
|
@@ -1324,6 +1536,8 @@ const Bool = {
|
|
|
1324
1536
|
* Returns `true` if every boolean is `true`, or for an empty array (vacuous truth).
|
|
1325
1537
|
* Short-circuits on the first `false`.
|
|
1326
1538
|
*
|
|
1539
|
+
* @see {@link Bool.any} to check if at least one boolean is true.
|
|
1540
|
+
*
|
|
1327
1541
|
* @example
|
|
1328
1542
|
* ```ts
|
|
1329
1543
|
* Bool.all([true, true, true]); // true
|
|
@@ -1337,6 +1551,8 @@ const Bool = {
|
|
|
1337
1551
|
* Returns `true` if at least one boolean is `true`. Returns `false` for an empty array.
|
|
1338
1552
|
* Short-circuits on the first `true`.
|
|
1339
1553
|
*
|
|
1554
|
+
* @see {@link Bool.all} to check if all booleans are true.
|
|
1555
|
+
*
|
|
1340
1556
|
* @example
|
|
1341
1557
|
* ```ts
|
|
1342
1558
|
* Bool.any([false, true, false]); // true
|
|
@@ -1351,6 +1567,8 @@ const Bool = {
|
|
|
1351
1567
|
* Positional ordering: `onFalse` first, `onTrue` second.
|
|
1352
1568
|
* Aligned with `Result.fold(onErr, onOk)` and `Maybe.fold(onNone, onSome)`.
|
|
1353
1569
|
*
|
|
1570
|
+
* @see {@link Bool.match} for named-case pattern matching with an object literal.
|
|
1571
|
+
*
|
|
1354
1572
|
* @example
|
|
1355
1573
|
* ```ts
|
|
1356
1574
|
* pipe(
|
|
@@ -1366,6 +1584,8 @@ const Bool = {
|
|
|
1366
1584
|
/**
|
|
1367
1585
|
* Pattern matching on boolean using named cases `{ true, false }`.
|
|
1368
1586
|
*
|
|
1587
|
+
* @see {@link Bool.fold} for positional argument pattern matching.
|
|
1588
|
+
*
|
|
1369
1589
|
* @example
|
|
1370
1590
|
* ```ts
|
|
1371
1591
|
* pipe(
|
|
@@ -1470,119 +1690,221 @@ const Bool = {
|
|
|
1470
1690
|
//#endregion
|
|
1471
1691
|
//#region src/Data/Dict.ts
|
|
1472
1692
|
const DictIs = {
|
|
1473
|
-
|
|
1474
|
-
|
|
1693
|
+
/**
|
|
1694
|
+
* Returns `true` when the dictionary contains zero entries.
|
|
1695
|
+
*
|
|
1696
|
+
* @see {@link nonEmpty} for checking if a dictionary contains entries.
|
|
1697
|
+
*/
|
|
1698
|
+
empty: (dict) => dict.size === 0,
|
|
1699
|
+
/**
|
|
1700
|
+
* Returns `true` when the dictionary contains at least one entry.
|
|
1701
|
+
*
|
|
1702
|
+
* @see {@link empty} for checking if a dictionary is empty.
|
|
1703
|
+
*/
|
|
1704
|
+
nonEmpty: (dict) => dict.size > 0
|
|
1475
1705
|
};
|
|
1476
1706
|
const empty$1 = () => new globalThis.Map();
|
|
1477
1707
|
const singleton$1 = (key, value) => new globalThis.Map([[key, value]]);
|
|
1478
1708
|
const DictFrom = {
|
|
1479
1709
|
entries: (entries) => new globalThis.Map(entries),
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
nullable: (data) => data === null || data === void 0 ? Maybe.make.none() : Maybe.make.some(data)
|
|
1710
|
+
record: (record) => new globalThis.Map(Object.entries(record)),
|
|
1711
|
+
nullable: (dict) => dict === null || dict === void 0 ? Maybe.make.none() : Maybe.make.some(dict)
|
|
1483
1712
|
};
|
|
1484
|
-
const DictTo = {
|
|
1713
|
+
const DictTo = { record: (dict) => {
|
|
1485
1714
|
const result = {};
|
|
1486
|
-
for (const [k, v] of
|
|
1715
|
+
for (const [k, v] of dict) result[k] = v;
|
|
1487
1716
|
return result;
|
|
1488
1717
|
} };
|
|
1489
|
-
const groupBy$1 = (
|
|
1718
|
+
const groupBy$1 = (keySelector) => (items) => {
|
|
1490
1719
|
const result = new globalThis.Map();
|
|
1491
|
-
for (const
|
|
1492
|
-
const k =
|
|
1720
|
+
for (const item of items) {
|
|
1721
|
+
const k = keySelector(item);
|
|
1493
1722
|
const existing = result.get(k);
|
|
1494
|
-
if (existing !== void 0) existing.push(
|
|
1495
|
-
else result.set(k, [
|
|
1723
|
+
if (existing !== void 0) existing.push(item);
|
|
1724
|
+
else result.set(k, [item]);
|
|
1496
1725
|
}
|
|
1497
1726
|
return result;
|
|
1498
1727
|
};
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
const
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1728
|
+
/**
|
|
1729
|
+
* Returns `true` when the dictionary contains the specified key.
|
|
1730
|
+
*
|
|
1731
|
+
* @see {@link lookup} for retrieving the value associated with a key.
|
|
1732
|
+
*/
|
|
1733
|
+
const has$1 = (key) => (dict) => dict.has(key);
|
|
1734
|
+
/**
|
|
1735
|
+
* Retrieves the value associated with a key wrapped in a `Maybe`.
|
|
1736
|
+
*
|
|
1737
|
+
* @see {@link has} for checking key existence without retrieving the value.
|
|
1738
|
+
*/
|
|
1739
|
+
const lookup$1 = (key) => (dict) => {
|
|
1740
|
+
const val = dict.get(key);
|
|
1741
|
+
return val !== void 0 || dict.has(key) ? Maybe.make.some(val) : Maybe.make.none();
|
|
1742
|
+
};
|
|
1743
|
+
const size$2 = (dict) => dict.size;
|
|
1744
|
+
/**
|
|
1745
|
+
* Returns all keys of a dictionary.
|
|
1746
|
+
*
|
|
1747
|
+
* @see {@link values} for extracting dictionary values.
|
|
1748
|
+
* @see {@link entries} for extracting key-value pairs.
|
|
1749
|
+
*/
|
|
1750
|
+
const keys$1 = (dict) => Array.from(dict.keys());
|
|
1751
|
+
/**
|
|
1752
|
+
* Returns all values of a dictionary.
|
|
1753
|
+
*
|
|
1754
|
+
* @see {@link keys} for extracting dictionary keys.
|
|
1755
|
+
* @see {@link entries} for extracting key-value pairs.
|
|
1756
|
+
*/
|
|
1757
|
+
const values$1 = (dict) => Array.from(dict.values());
|
|
1758
|
+
/**
|
|
1759
|
+
* Returns all key-value pairs of a dictionary.
|
|
1760
|
+
*
|
|
1761
|
+
* @see {@link keys} for extracting dictionary keys.
|
|
1762
|
+
* @see {@link values} for extracting dictionary values.
|
|
1763
|
+
*/
|
|
1764
|
+
const entries$1 = (dict) => Array.from(dict.entries());
|
|
1765
|
+
/**
|
|
1766
|
+
* Returns a new dictionary with the key set to value.
|
|
1767
|
+
*
|
|
1768
|
+
* @see {@link remove} for deleting a key from a dictionary.
|
|
1769
|
+
* @see {@link upsert} for conditional insertion/update.
|
|
1770
|
+
*/
|
|
1771
|
+
const insert = (key, value) => (dict) => {
|
|
1772
|
+
const res = new globalThis.Map(dict);
|
|
1510
1773
|
res.set(key, value);
|
|
1511
1774
|
return res;
|
|
1512
1775
|
};
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1776
|
+
/**
|
|
1777
|
+
* Returns a new dictionary with the specified key removed.
|
|
1778
|
+
*
|
|
1779
|
+
* @see {@link insert} for adding or replacing a key in a dictionary.
|
|
1780
|
+
*/
|
|
1781
|
+
const remove$1 = (key) => (dict) => {
|
|
1782
|
+
if (!dict.has(key)) return dict;
|
|
1783
|
+
const res = new globalThis.Map(dict);
|
|
1516
1784
|
res.delete(key);
|
|
1517
1785
|
return res;
|
|
1518
1786
|
};
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1787
|
+
/**
|
|
1788
|
+
* Inserts or updates a key using a callback that receives the existing value if present.
|
|
1789
|
+
*
|
|
1790
|
+
* @see {@link insert} for unconditional key setting.
|
|
1791
|
+
*/
|
|
1792
|
+
const upsert = (key, update) => (dict) => {
|
|
1793
|
+
const res = new globalThis.Map(dict);
|
|
1794
|
+
const existing = dict.has(key) ? Maybe.make.some(dict.get(key)) : Maybe.make.none();
|
|
1795
|
+
res.set(key, update(existing));
|
|
1523
1796
|
return res;
|
|
1524
1797
|
};
|
|
1525
|
-
|
|
1798
|
+
/**
|
|
1799
|
+
* Transforms each value in the dictionary.
|
|
1800
|
+
*
|
|
1801
|
+
* @see {@link mapWithKey} for transforming values with key access.
|
|
1802
|
+
*/
|
|
1803
|
+
const map$2 = (transform) => (dict) => {
|
|
1526
1804
|
const res = new globalThis.Map();
|
|
1527
|
-
for (const [k, v] of
|
|
1805
|
+
for (const [k, v] of dict) res.set(k, transform(v));
|
|
1528
1806
|
return res;
|
|
1529
1807
|
};
|
|
1530
|
-
|
|
1808
|
+
/**
|
|
1809
|
+
* Transforms each value in the dictionary, also receiving the key.
|
|
1810
|
+
*
|
|
1811
|
+
* @see {@link map} for transforming values without key access.
|
|
1812
|
+
*/
|
|
1813
|
+
const mapWithKey$1 = (transform) => (dict) => {
|
|
1531
1814
|
const res = new globalThis.Map();
|
|
1532
|
-
for (const [k, v] of
|
|
1815
|
+
for (const [k, v] of dict) res.set(k, transform(k, v));
|
|
1533
1816
|
return res;
|
|
1534
1817
|
};
|
|
1535
|
-
|
|
1818
|
+
/**
|
|
1819
|
+
* Filters dictionary entries by a predicate on values.
|
|
1820
|
+
*
|
|
1821
|
+
* @see {@link filterWithKey} for filtering with key access.
|
|
1822
|
+
*/
|
|
1823
|
+
const filter$2 = (predicate) => (dict) => {
|
|
1536
1824
|
const res = new globalThis.Map();
|
|
1537
|
-
for (const [k, v] of
|
|
1825
|
+
for (const [k, v] of dict) if (predicate(v)) res.set(k, v);
|
|
1538
1826
|
return res;
|
|
1539
1827
|
};
|
|
1540
|
-
|
|
1828
|
+
/**
|
|
1829
|
+
* Filters dictionary entries by a predicate that also receives the key.
|
|
1830
|
+
*
|
|
1831
|
+
* @see {@link filter} for filtering values without key access.
|
|
1832
|
+
*/
|
|
1833
|
+
const filterWithKey$1 = (predicate) => (dict) => {
|
|
1541
1834
|
const res = new globalThis.Map();
|
|
1542
|
-
for (const [k, v] of
|
|
1835
|
+
for (const [k, v] of dict) if (predicate(k, v)) res.set(k, v);
|
|
1543
1836
|
return res;
|
|
1544
1837
|
};
|
|
1545
|
-
|
|
1838
|
+
/**
|
|
1839
|
+
* Removes all `None` values from a dictionary, unwrapping `Some` values.
|
|
1840
|
+
*/
|
|
1841
|
+
const compact$1 = (dict) => {
|
|
1546
1842
|
const res = new globalThis.Map();
|
|
1547
|
-
for (const [k, v] of
|
|
1843
|
+
for (const [k, v] of dict) if (v.kind === "Some") res.set(k, v.value);
|
|
1548
1844
|
return res;
|
|
1549
1845
|
};
|
|
1550
|
-
|
|
1846
|
+
/**
|
|
1847
|
+
* Transforms values with a function returning `Maybe`, keeping only `Some` values.
|
|
1848
|
+
*
|
|
1849
|
+
* @see {@link filter} for filtering without transformation.
|
|
1850
|
+
*/
|
|
1851
|
+
const filterMap$2 = (transform) => (dict) => {
|
|
1551
1852
|
const res = new globalThis.Map();
|
|
1552
|
-
for (const [k, v] of
|
|
1553
|
-
const mb =
|
|
1853
|
+
for (const [k, v] of dict) {
|
|
1854
|
+
const mb = transform(v);
|
|
1554
1855
|
if (mb.kind === "Some") res.set(k, mb.value);
|
|
1555
1856
|
}
|
|
1556
1857
|
return res;
|
|
1557
1858
|
};
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1859
|
+
/**
|
|
1860
|
+
* Combines two dictionaries, preferring entries from `other` on key collisions.
|
|
1861
|
+
*
|
|
1862
|
+
* @see {@link intersection} for keeping only common keys.
|
|
1863
|
+
* @see {@link difference} for removing keys present in the other dictionary.
|
|
1864
|
+
*/
|
|
1865
|
+
const union$1 = (other) => (dict) => {
|
|
1866
|
+
if (dict.size === 0) return other;
|
|
1867
|
+
if (other.size === 0) return dict;
|
|
1868
|
+
const res = new globalThis.Map(dict);
|
|
1562
1869
|
for (const [k, v] of other) res.set(k, v);
|
|
1563
1870
|
return res;
|
|
1564
1871
|
};
|
|
1565
|
-
|
|
1872
|
+
/**
|
|
1873
|
+
* Returns a new dictionary containing only keys present in both dictionaries.
|
|
1874
|
+
*
|
|
1875
|
+
* @see {@link union} for combining all keys from both dictionaries.
|
|
1876
|
+
* @see {@link difference} for subtracting keys.
|
|
1877
|
+
*/
|
|
1878
|
+
const intersection$1 = (other) => (dict) => {
|
|
1566
1879
|
const res = new globalThis.Map();
|
|
1567
|
-
for (const [k, v] of
|
|
1880
|
+
for (const [k, v] of dict) if (other.has(k)) res.set(k, v);
|
|
1568
1881
|
return res;
|
|
1569
1882
|
};
|
|
1570
|
-
|
|
1571
|
-
|
|
1883
|
+
/**
|
|
1884
|
+
* Returns a new dictionary containing keys from `dict` that are not in `other`.
|
|
1885
|
+
*
|
|
1886
|
+
* @see {@link union} for combining all keys.
|
|
1887
|
+
* @see {@link intersection} for keeping only common keys.
|
|
1888
|
+
*/
|
|
1889
|
+
const difference$1 = (other) => (dict) => {
|
|
1890
|
+
if (other.size === 0) return dict;
|
|
1572
1891
|
const res = new globalThis.Map();
|
|
1573
|
-
for (const [k, v] of
|
|
1892
|
+
for (const [k, v] of dict) if (!other.has(k)) res.set(k, v);
|
|
1574
1893
|
return res;
|
|
1575
1894
|
};
|
|
1576
|
-
const reduce$1 = (
|
|
1577
|
-
let acc =
|
|
1578
|
-
for (const [, v] of
|
|
1895
|
+
const reduce$1 = (initial, reducer) => (dict) => {
|
|
1896
|
+
let acc = initial;
|
|
1897
|
+
for (const [, v] of dict) acc = reducer(acc, v);
|
|
1579
1898
|
return acc;
|
|
1580
1899
|
};
|
|
1581
|
-
const reduceWithKey = (
|
|
1582
|
-
let acc =
|
|
1583
|
-
for (const [k, v] of
|
|
1900
|
+
const reduceWithKey = (initial, reducer) => (dict) => {
|
|
1901
|
+
let acc = initial;
|
|
1902
|
+
for (const [k, v] of dict) acc = reducer(acc, v, k);
|
|
1584
1903
|
return acc;
|
|
1585
1904
|
};
|
|
1905
|
+
/**
|
|
1906
|
+
* Merges two dictionaries using a combination function on collisions.
|
|
1907
|
+
*/
|
|
1586
1908
|
function mergeWith$1(combine) {
|
|
1587
1909
|
return ((arg1, arg2) => {
|
|
1588
1910
|
if (arg2 !== void 0) {
|
|
@@ -1600,27 +1922,27 @@ function mergeWith$1(combine) {
|
|
|
1600
1922
|
};
|
|
1601
1923
|
});
|
|
1602
1924
|
}
|
|
1603
|
-
const mapEntries$1 = (
|
|
1925
|
+
const mapEntries$1 = (transform) => (dict) => {
|
|
1604
1926
|
const res = new globalThis.Map();
|
|
1605
|
-
for (const [k, v] of
|
|
1606
|
-
const [nk, nv] =
|
|
1927
|
+
for (const [k, v] of dict) {
|
|
1928
|
+
const [nk, nv] = transform(k, v);
|
|
1607
1929
|
res.set(nk, nv);
|
|
1608
1930
|
}
|
|
1609
1931
|
return res;
|
|
1610
1932
|
};
|
|
1611
|
-
const mapKeys$1 = (
|
|
1933
|
+
const mapKeys$1 = (transform) => (dict) => {
|
|
1612
1934
|
const res = new globalThis.Map();
|
|
1613
|
-
for (const [k, v] of
|
|
1935
|
+
for (const [k, v] of dict) res.set(transform(k), v);
|
|
1614
1936
|
return res;
|
|
1615
1937
|
};
|
|
1616
1938
|
const _nonEmptySingleton = (key, value) => new globalThis.Map([[key, value]]);
|
|
1617
|
-
const _nonEmptyFromMap = (
|
|
1618
|
-
const _nonEmptyKeys = (
|
|
1619
|
-
const _nonEmptyValues = (
|
|
1620
|
-
const _nonEmptyEntries = (
|
|
1621
|
-
const _nonEmptyReduce = (
|
|
1622
|
-
const _nonEmptyMap = (
|
|
1623
|
-
const _nonEmptyMapWithKey = (
|
|
1939
|
+
const _nonEmptyFromMap = (dict) => dict.size > 0 ? Maybe.make.some(dict) : Maybe.make.none();
|
|
1940
|
+
const _nonEmptyKeys = (dict) => keys$1(dict);
|
|
1941
|
+
const _nonEmptyValues = (dict) => values$1(dict);
|
|
1942
|
+
const _nonEmptyEntries = (dict) => entries$1(dict);
|
|
1943
|
+
const _nonEmptyReduce = (reducer) => (dict) => _nonEmptyValues(dict).reduce(reducer);
|
|
1944
|
+
const _nonEmptyMap = (transform) => (dict) => map$2(transform)(dict);
|
|
1945
|
+
const _nonEmptyMapWithKey = (transform) => (dict) => mapWithKey$1(transform)(dict);
|
|
1624
1946
|
const Dict = {
|
|
1625
1947
|
is: DictIs,
|
|
1626
1948
|
empty: empty$1,
|
|
@@ -1653,7 +1975,7 @@ const Dict = {
|
|
|
1653
1975
|
mapKeys: mapKeys$1,
|
|
1654
1976
|
NonEmpty: {
|
|
1655
1977
|
singleton: _nonEmptySingleton,
|
|
1656
|
-
from: {
|
|
1978
|
+
from: { map: _nonEmptyFromMap },
|
|
1657
1979
|
keys: _nonEmptyKeys,
|
|
1658
1980
|
values: _nonEmptyValues,
|
|
1659
1981
|
entries: _nonEmptyEntries,
|
|
@@ -1706,9 +2028,9 @@ const Json = {
|
|
|
1706
2028
|
};
|
|
1707
2029
|
//#endregion
|
|
1708
2030
|
//#region src/Data/Num.ts
|
|
1709
|
-
const sumFn = (
|
|
2031
|
+
const sumFn = (numbers) => {
|
|
1710
2032
|
let result = 0;
|
|
1711
|
-
for (let i = 0; i <
|
|
2033
|
+
for (let i = 0; i < numbers.length; i++) result += numbers[i];
|
|
1712
2034
|
return result;
|
|
1713
2035
|
};
|
|
1714
2036
|
const Num = {
|
|
@@ -1722,27 +2044,31 @@ const Num = {
|
|
|
1722
2044
|
* Num.is.zero(5); // false
|
|
1723
2045
|
* ```
|
|
1724
2046
|
*/
|
|
1725
|
-
zero: (
|
|
2047
|
+
zero: (value) => value === 0,
|
|
1726
2048
|
/**
|
|
1727
2049
|
* Returns `true` when the number is a whole integer.
|
|
1728
2050
|
*
|
|
2051
|
+
* @see {@link Num.is.float} to check for fractional numbers.
|
|
2052
|
+
*
|
|
1729
2053
|
* @example
|
|
1730
2054
|
* ```ts
|
|
1731
2055
|
* Num.is.integer(5); // true
|
|
1732
2056
|
* Num.is.integer(3.14); // false
|
|
1733
2057
|
* ```
|
|
1734
2058
|
*/
|
|
1735
|
-
integer: (
|
|
2059
|
+
integer: (value) => Number.isInteger(value),
|
|
1736
2060
|
/**
|
|
1737
2061
|
* Returns `true` when the number is a finite float (fractional number).
|
|
1738
2062
|
*
|
|
2063
|
+
* @see {@link Num.is.integer} to check for whole numbers.
|
|
2064
|
+
*
|
|
1739
2065
|
* @example
|
|
1740
2066
|
* ```ts
|
|
1741
2067
|
* Num.is.float(3.14); // true
|
|
1742
2068
|
* Num.is.float(5); // false
|
|
1743
2069
|
* ```
|
|
1744
2070
|
*/
|
|
1745
|
-
float: (
|
|
2071
|
+
float: (value) => Number.isFinite(value) && !Number.isInteger(value),
|
|
1746
2072
|
/**
|
|
1747
2073
|
* Returns `true` when the number is finite (not `Infinity`, `-Infinity`, or `NaN`).
|
|
1748
2074
|
*
|
|
@@ -1752,7 +2078,7 @@ const Num = {
|
|
|
1752
2078
|
* Num.is.finite(Infinity); // false
|
|
1753
2079
|
* ```
|
|
1754
2080
|
*/
|
|
1755
|
-
finite: (
|
|
2081
|
+
finite: (value) => Number.isFinite(value),
|
|
1756
2082
|
/**
|
|
1757
2083
|
* Returns `true` when the value is `NaN`.
|
|
1758
2084
|
*
|
|
@@ -1762,10 +2088,12 @@ const Num = {
|
|
|
1762
2088
|
* Num.is.nan(42); // false
|
|
1763
2089
|
* ```
|
|
1764
2090
|
*/
|
|
1765
|
-
nan: (
|
|
2091
|
+
nan: (value) => Number.isNaN(value),
|
|
1766
2092
|
/**
|
|
1767
2093
|
* Returns `true` when the number is an even integer.
|
|
1768
2094
|
*
|
|
2095
|
+
* @see {@link Num.is.odd} to check if a number is odd.
|
|
2096
|
+
*
|
|
1769
2097
|
* @example
|
|
1770
2098
|
* ```ts
|
|
1771
2099
|
* Num.is.even(4); // true
|
|
@@ -1773,10 +2101,12 @@ const Num = {
|
|
|
1773
2101
|
* Num.is.even(2.5); // false
|
|
1774
2102
|
* ```
|
|
1775
2103
|
*/
|
|
1776
|
-
even: (
|
|
2104
|
+
even: (value) => Number.isInteger(value) && value % 2 === 0,
|
|
1777
2105
|
/**
|
|
1778
2106
|
* Returns `true` when the number is an odd integer.
|
|
1779
2107
|
*
|
|
2108
|
+
* @see {@link Num.is.even} to check if a number is even.
|
|
2109
|
+
*
|
|
1780
2110
|
* @example
|
|
1781
2111
|
* ```ts
|
|
1782
2112
|
* Num.is.odd(3); // true
|
|
@@ -1784,10 +2114,12 @@ const Num = {
|
|
|
1784
2114
|
* Num.is.odd(2.5); // false
|
|
1785
2115
|
* ```
|
|
1786
2116
|
*/
|
|
1787
|
-
odd: (
|
|
2117
|
+
odd: (value) => Number.isInteger(value) && value % 2 !== 0,
|
|
1788
2118
|
/**
|
|
1789
2119
|
* Returns `true` when the number is strictly greater than zero.
|
|
1790
2120
|
*
|
|
2121
|
+
* @see {@link Num.is.negative} to check if a number is less than zero.
|
|
2122
|
+
*
|
|
1791
2123
|
* @example
|
|
1792
2124
|
* ```ts
|
|
1793
2125
|
* Num.is.positive(5); // true
|
|
@@ -1795,10 +2127,12 @@ const Num = {
|
|
|
1795
2127
|
* Num.is.positive(-5); // false
|
|
1796
2128
|
* ```
|
|
1797
2129
|
*/
|
|
1798
|
-
positive: (
|
|
2130
|
+
positive: (value) => value > 0,
|
|
1799
2131
|
/**
|
|
1800
2132
|
* Returns `true` when the number is strictly less than zero.
|
|
1801
2133
|
*
|
|
2134
|
+
* @see {@link Num.is.positive} to check if a number is greater than zero.
|
|
2135
|
+
*
|
|
1802
2136
|
* @example
|
|
1803
2137
|
* ```ts
|
|
1804
2138
|
* Num.is.negative(-5); // true
|
|
@@ -1806,7 +2140,7 @@ const Num = {
|
|
|
1806
2140
|
* Num.is.negative(5); // false
|
|
1807
2141
|
* ```
|
|
1808
2142
|
*/
|
|
1809
|
-
negative: (
|
|
2143
|
+
negative: (value) => value < 0
|
|
1810
2144
|
},
|
|
1811
2145
|
/**
|
|
1812
2146
|
* Generates an array of numbers from `from` to `to` (both inclusive),
|
|
@@ -1840,10 +2174,12 @@ const Num = {
|
|
|
1840
2174
|
* pipe(42, Num.clamp(0, 100)); // 42
|
|
1841
2175
|
* ```
|
|
1842
2176
|
*/
|
|
1843
|
-
clamp: (min, max) => (
|
|
2177
|
+
clamp: (min, max) => (value) => Math.min(Math.max(value, min), max),
|
|
1844
2178
|
/**
|
|
1845
2179
|
* Returns `true` when the number is between `min` and `max` (both inclusive).
|
|
1846
2180
|
*
|
|
2181
|
+
* @see {@link Num.inRange} for half-open range checking [start, end).
|
|
2182
|
+
*
|
|
1847
2183
|
* @example
|
|
1848
2184
|
* ```ts
|
|
1849
2185
|
* pipe(5, Num.between(1, 10)); // true
|
|
@@ -1851,10 +2187,12 @@ const Num = {
|
|
|
1851
2187
|
* pipe(10, Num.between(1, 10)); // true
|
|
1852
2188
|
* ```
|
|
1853
2189
|
*/
|
|
1854
|
-
between: (min, max) => (
|
|
2190
|
+
between: (min, max) => (value) => value >= min && value <= max,
|
|
1855
2191
|
/**
|
|
1856
2192
|
* Returns `true` when the number is in the range `[start, end)` (inclusive of `start`, exclusive of `end`).
|
|
1857
2193
|
*
|
|
2194
|
+
* @see {@link Num.between} for fully closed range checking [min, max].
|
|
2195
|
+
*
|
|
1858
2196
|
* @example
|
|
1859
2197
|
* ```ts
|
|
1860
2198
|
* pipe(5, Num.inRange(1, 10)); // true
|
|
@@ -1862,7 +2200,7 @@ const Num = {
|
|
|
1862
2200
|
* pipe(10, Num.inRange(1, 10)); // false
|
|
1863
2201
|
* ```
|
|
1864
2202
|
*/
|
|
1865
|
-
inRange: (start, end) => (
|
|
2203
|
+
inRange: (start, end) => (value) => value >= start && value < end,
|
|
1866
2204
|
/**
|
|
1867
2205
|
* Parses a string as a number. Returns `None` when the result is `NaN`.
|
|
1868
2206
|
*
|
|
@@ -1874,13 +2212,15 @@ const Num = {
|
|
|
1874
2212
|
* Num.parse(""); // None
|
|
1875
2213
|
* ```
|
|
1876
2214
|
*/
|
|
1877
|
-
parse: (
|
|
1878
|
-
if (
|
|
1879
|
-
const n = Number(
|
|
2215
|
+
parse: (text) => {
|
|
2216
|
+
if (text.trim() === "") return Maybe.make.none();
|
|
2217
|
+
const n = Number(text);
|
|
1880
2218
|
return isNaN(n) ? Maybe.make.none() : Maybe.make.some(n);
|
|
1881
2219
|
},
|
|
1882
2220
|
/**
|
|
1883
|
-
* Adds `
|
|
2221
|
+
* Adds `amount` to a number. Data-last: use in `pipe` or `Arr.map`.
|
|
2222
|
+
*
|
|
2223
|
+
* @see {@link Num.subtract} to subtract an amount from a number.
|
|
1884
2224
|
*
|
|
1885
2225
|
* @example
|
|
1886
2226
|
* ```ts
|
|
@@ -1888,9 +2228,11 @@ const Num = {
|
|
|
1888
2228
|
* pipe([1, 2, 3], Arr.map(Num.add(10))); // [11, 12, 13]
|
|
1889
2229
|
* ```
|
|
1890
2230
|
*/
|
|
1891
|
-
add: (
|
|
2231
|
+
add: (amount) => (value) => value + amount,
|
|
1892
2232
|
/**
|
|
1893
|
-
* Subtracts `
|
|
2233
|
+
* Subtracts `amount` from a number. Data-last: `subtract(amount)(from)` = `from - amount`.
|
|
2234
|
+
*
|
|
2235
|
+
* @see {@link Num.add} to add an amount to a number.
|
|
1894
2236
|
*
|
|
1895
2237
|
* @example
|
|
1896
2238
|
* ```ts
|
|
@@ -1898,9 +2240,11 @@ const Num = {
|
|
|
1898
2240
|
* pipe([5, 10, 15], Arr.map(Num.subtract(2))); // [3, 8, 13]
|
|
1899
2241
|
* ```
|
|
1900
2242
|
*/
|
|
1901
|
-
subtract: (
|
|
2243
|
+
subtract: (amount) => (from) => from - amount,
|
|
1902
2244
|
/**
|
|
1903
|
-
* Multiplies a number by `
|
|
2245
|
+
* Multiplies a number by `factor`. Data-last: use in `pipe` or `Arr.map`.
|
|
2246
|
+
*
|
|
2247
|
+
* @see {@link Num.divide} to divide a number by a divisor.
|
|
1904
2248
|
*
|
|
1905
2249
|
* @example
|
|
1906
2250
|
* ```ts
|
|
@@ -1908,9 +2252,12 @@ const Num = {
|
|
|
1908
2252
|
* pipe([1, 2, 3], Arr.map(Num.multiply(100))); // [100, 200, 300]
|
|
1909
2253
|
* ```
|
|
1910
2254
|
*/
|
|
1911
|
-
multiply: (
|
|
2255
|
+
multiply: (factor) => (value) => value * factor,
|
|
1912
2256
|
/**
|
|
1913
|
-
* Divides a number by `
|
|
2257
|
+
* Divides a number by `divisor`. Returns `None` when `divisor` is zero. Data-last: `divide(divisor)(dividend)` = `dividend / divisor`.
|
|
2258
|
+
*
|
|
2259
|
+
* @see {@link Num.multiply} to multiply a number by a factor.
|
|
2260
|
+
* @see {@link Num.remainder} to compute the division remainder.
|
|
1914
2261
|
*
|
|
1915
2262
|
* @example
|
|
1916
2263
|
* ```ts
|
|
@@ -1919,7 +2266,7 @@ const Num = {
|
|
|
1919
2266
|
* pipe([10, 20, 30], Arr.filterMap(Num.divide(10))); // [1, 2, 3]
|
|
1920
2267
|
* ```
|
|
1921
2268
|
*/
|
|
1922
|
-
divide: (
|
|
2269
|
+
divide: (divisor) => (dividend) => divisor === 0 ? Maybe.make.none() : Maybe.make.some(dividend / divisor),
|
|
1923
2270
|
/**
|
|
1924
2271
|
* Returns the absolute value of a number.
|
|
1925
2272
|
*
|
|
@@ -1929,7 +2276,7 @@ const Num = {
|
|
|
1929
2276
|
* pipe(5, Num.abs); // 5
|
|
1930
2277
|
* ```
|
|
1931
2278
|
*/
|
|
1932
|
-
abs: (
|
|
2279
|
+
abs: (value) => Math.abs(value),
|
|
1933
2280
|
/**
|
|
1934
2281
|
* Negates a number (arithmetic negation).
|
|
1935
2282
|
*
|
|
@@ -1939,40 +2286,51 @@ const Num = {
|
|
|
1939
2286
|
* pipe(-5, Num.negate); // 5
|
|
1940
2287
|
* ```
|
|
1941
2288
|
*/
|
|
1942
|
-
negate: (
|
|
2289
|
+
negate: (value) => -value,
|
|
1943
2290
|
/**
|
|
1944
2291
|
* Rounds a number to the nearest integer.
|
|
1945
2292
|
*
|
|
2293
|
+
* @see {@link Num.floor} to round down.
|
|
2294
|
+
* @see {@link Num.ceil} to round up.
|
|
2295
|
+
*
|
|
1946
2296
|
* @example
|
|
1947
2297
|
* ```ts
|
|
1948
2298
|
* pipe(3.5, Num.round); // 4
|
|
1949
2299
|
* pipe(3.4, Num.round); // 3
|
|
1950
2300
|
* ```
|
|
1951
2301
|
*/
|
|
1952
|
-
round: (
|
|
2302
|
+
round: (value) => Math.round(value),
|
|
1953
2303
|
/**
|
|
1954
2304
|
* Rounds a number down to the nearest integer.
|
|
1955
2305
|
*
|
|
2306
|
+
* @see {@link Num.round} to round to nearest integer.
|
|
2307
|
+
* @see {@link Num.ceil} to round up.
|
|
2308
|
+
*
|
|
1956
2309
|
* @example
|
|
1957
2310
|
* ```ts
|
|
1958
2311
|
* pipe(3.9, Num.floor); // 3
|
|
1959
2312
|
* pipe(-3.2, Num.floor); // -4
|
|
1960
2313
|
* ```
|
|
1961
2314
|
*/
|
|
1962
|
-
floor: (
|
|
2315
|
+
floor: (value) => Math.floor(value),
|
|
1963
2316
|
/**
|
|
1964
2317
|
* Rounds a number up to the nearest integer.
|
|
1965
2318
|
*
|
|
2319
|
+
* @see {@link Num.round} to round to nearest integer.
|
|
2320
|
+
* @see {@link Num.floor} to round down.
|
|
2321
|
+
*
|
|
1966
2322
|
* @example
|
|
1967
2323
|
* ```ts
|
|
1968
2324
|
* pipe(3.1, Num.ceil); // 4
|
|
1969
2325
|
* pipe(-3.9, Num.ceil); // -3
|
|
1970
2326
|
* ```
|
|
1971
2327
|
*/
|
|
1972
|
-
ceil: (
|
|
2328
|
+
ceil: (value) => Math.ceil(value),
|
|
1973
2329
|
/**
|
|
1974
2330
|
* Returns the remainder of dividing a number by `divisor`. Returns `None` when `divisor` is zero.
|
|
1975
|
-
* Data-last: `remainder(divisor)(
|
|
2331
|
+
* Data-last: `remainder(divisor)(dividend)` = `dividend % divisor`.
|
|
2332
|
+
*
|
|
2333
|
+
* @see {@link Num.divide} for full division.
|
|
1976
2334
|
*
|
|
1977
2335
|
* @example
|
|
1978
2336
|
* ```ts
|
|
@@ -1981,7 +2339,7 @@ const Num = {
|
|
|
1981
2339
|
* pipe([10, 11, 12], Arr.filterMap(Num.remainder(3))); // [1, 2, 0]
|
|
1982
2340
|
* ```
|
|
1983
2341
|
*/
|
|
1984
|
-
remainder: (divisor) => (
|
|
2342
|
+
remainder: (divisor) => (dividend) => divisor === 0 ? Maybe.make.none() : Maybe.make.some(dividend % divisor),
|
|
1985
2343
|
/**
|
|
1986
2344
|
* Computes the sum of a list of numbers. Returns `0` if the list is empty.
|
|
1987
2345
|
*
|
|
@@ -2001,39 +2359,43 @@ const Num = {
|
|
|
2001
2359
|
* Num.mean([]); // None
|
|
2002
2360
|
* ```
|
|
2003
2361
|
*/
|
|
2004
|
-
mean: (
|
|
2362
|
+
mean: (numbers) => numbers.length === 0 ? Maybe.make.none() : Maybe.make.some(sumFn(numbers) / numbers.length),
|
|
2005
2363
|
/**
|
|
2006
2364
|
* Computes the minimum of a list of numbers. Returns `None` if the list is empty.
|
|
2007
2365
|
*
|
|
2366
|
+
* @see {@link Num.max} to compute the maximum value.
|
|
2367
|
+
*
|
|
2008
2368
|
* @example
|
|
2009
2369
|
* ```ts
|
|
2010
2370
|
* Num.min([5, 1, 3]); // Some(1)
|
|
2011
2371
|
* Num.min([]); // None
|
|
2012
2372
|
* ```
|
|
2013
2373
|
*/
|
|
2014
|
-
min: (
|
|
2015
|
-
if (
|
|
2016
|
-
let [result] =
|
|
2017
|
-
for (let i = 1; i <
|
|
2374
|
+
min: (numbers) => {
|
|
2375
|
+
if (numbers.length === 0) return Maybe.make.none();
|
|
2376
|
+
let [result] = numbers;
|
|
2377
|
+
for (let i = 1; i < numbers.length; i++) if (numbers[i] < result) result = numbers[i];
|
|
2018
2378
|
return Maybe.make.some(result);
|
|
2019
2379
|
},
|
|
2020
2380
|
/**
|
|
2021
2381
|
* Computes the maximum of a list of numbers. Returns `None` if the list is empty.
|
|
2022
2382
|
*
|
|
2383
|
+
* @see {@link Num.min} to compute the minimum value.
|
|
2384
|
+
*
|
|
2023
2385
|
* @example
|
|
2024
2386
|
* ```ts
|
|
2025
2387
|
* Num.max([1, 5, 3]); // Some(5)
|
|
2026
2388
|
* Num.max([]); // None
|
|
2027
2389
|
* ```
|
|
2028
2390
|
*/
|
|
2029
|
-
max: (
|
|
2030
|
-
if (
|
|
2031
|
-
let [result] =
|
|
2032
|
-
for (let i = 1; i <
|
|
2391
|
+
max: (numbers) => {
|
|
2392
|
+
if (numbers.length === 0) return Maybe.make.none();
|
|
2393
|
+
let [result] = numbers;
|
|
2394
|
+
for (let i = 1; i < numbers.length; i++) if (numbers[i] > result) result = numbers[i];
|
|
2033
2395
|
return Maybe.make.some(result);
|
|
2034
2396
|
},
|
|
2035
2397
|
/**
|
|
2036
|
-
* Formats a number using `Intl.NumberFormat`. Returns `None` when `
|
|
2398
|
+
* Formats a number using `Intl.NumberFormat`. Returns `None` when `value` is `NaN` or non-finite.
|
|
2037
2399
|
* Data-last curried signature.
|
|
2038
2400
|
*
|
|
2039
2401
|
* @example
|
|
@@ -2043,11 +2405,11 @@ const Num = {
|
|
|
2043
2405
|
* pipe(NaN, formatCurrency); // None
|
|
2044
2406
|
* ```
|
|
2045
2407
|
*/
|
|
2046
|
-
format: (options, locales) => (
|
|
2408
|
+
format: (options, locales) => (value) => !Number.isFinite(value) ? Maybe.make.none() : Maybe.make.some(new Intl.NumberFormat(locales, options).format(value))
|
|
2047
2409
|
};
|
|
2048
2410
|
//#endregion
|
|
2049
2411
|
//#region src/Data/Rec.ts
|
|
2050
|
-
const _isNonEmpty = (
|
|
2412
|
+
const _isNonEmpty = (record) => Object.keys(record).length > 0;
|
|
2051
2413
|
const _setKey = (record, key, value) => {
|
|
2052
2414
|
if (key === "__proto__") Object.defineProperty(record, key, {
|
|
2053
2415
|
value,
|
|
@@ -2059,8 +2421,8 @@ const _setKey = (record, key, value) => {
|
|
|
2059
2421
|
};
|
|
2060
2422
|
let RecMaybe;
|
|
2061
2423
|
(function(_RecMaybe) {
|
|
2062
|
-
const traverse = _RecMaybe.traverse = (
|
|
2063
|
-
const recordKeys = Object.keys(
|
|
2424
|
+
const traverse = _RecMaybe.traverse = (transform) => (record) => {
|
|
2425
|
+
const recordKeys = Object.keys(record);
|
|
2064
2426
|
if (recordKeys.length === 0) return {
|
|
2065
2427
|
kind: "Some",
|
|
2066
2428
|
value: {}
|
|
@@ -2068,7 +2430,7 @@ let RecMaybe;
|
|
|
2068
2430
|
const result = {};
|
|
2069
2431
|
for (let i = 0; i < recordKeys.length; i++) {
|
|
2070
2432
|
const key = recordKeys[i];
|
|
2071
|
-
const maybeVal =
|
|
2433
|
+
const maybeVal = transform(record[key]);
|
|
2072
2434
|
if (maybeVal.kind === "None") return maybeVal;
|
|
2073
2435
|
_setKey(result, key, maybeVal.value);
|
|
2074
2436
|
}
|
|
@@ -2077,16 +2439,16 @@ let RecMaybe;
|
|
|
2077
2439
|
value: result
|
|
2078
2440
|
};
|
|
2079
2441
|
};
|
|
2080
|
-
_RecMaybe.sequence = (
|
|
2442
|
+
_RecMaybe.sequence = (record) => traverse((value) => value)(record);
|
|
2081
2443
|
})(RecMaybe || (RecMaybe = {}));
|
|
2082
2444
|
let RecResult;
|
|
2083
2445
|
(function(_RecResult) {
|
|
2084
|
-
const traverse = _RecResult.traverse = (
|
|
2085
|
-
const recordKeys = Object.keys(
|
|
2446
|
+
const traverse = _RecResult.traverse = (transform) => (record) => {
|
|
2447
|
+
const recordKeys = Object.keys(record);
|
|
2086
2448
|
const result = {};
|
|
2087
2449
|
for (let i = 0; i < recordKeys.length; i++) {
|
|
2088
2450
|
const key = recordKeys[i];
|
|
2089
|
-
const res =
|
|
2451
|
+
const res = transform(record[key]);
|
|
2090
2452
|
if (res.kind === "Err") return res;
|
|
2091
2453
|
_setKey(result, key, res.value);
|
|
2092
2454
|
}
|
|
@@ -2095,7 +2457,7 @@ let RecResult;
|
|
|
2095
2457
|
value: result
|
|
2096
2458
|
};
|
|
2097
2459
|
};
|
|
2098
|
-
_RecResult.sequence = (
|
|
2460
|
+
_RecResult.sequence = (record) => traverse((value) => value)(record);
|
|
2099
2461
|
})(RecResult || (RecResult = {}));
|
|
2100
2462
|
/**
|
|
2101
2463
|
* Functional record/object utilities that compose well with pipe.
|
|
@@ -2117,16 +2479,20 @@ const RecIs = {
|
|
|
2117
2479
|
/**
|
|
2118
2480
|
* Returns true if the record has no keys.
|
|
2119
2481
|
*
|
|
2482
|
+
* @see {@link nonEmpty} for checking if a record has keys.
|
|
2483
|
+
*
|
|
2120
2484
|
* @example
|
|
2121
2485
|
* ```ts
|
|
2122
2486
|
* Rec.is.empty({}); // true
|
|
2123
2487
|
* Rec.is.empty({ a: 1 }); // false
|
|
2124
2488
|
* ```
|
|
2125
2489
|
*/
|
|
2126
|
-
empty: (
|
|
2490
|
+
empty: (record) => Object.keys(record).length === 0,
|
|
2127
2491
|
/**
|
|
2128
2492
|
* Type guard to check if a record is non-empty.
|
|
2129
2493
|
*
|
|
2494
|
+
* @see {@link empty} for checking if a record has no keys.
|
|
2495
|
+
*
|
|
2130
2496
|
* @example
|
|
2131
2497
|
* ```ts
|
|
2132
2498
|
* Rec.is.nonEmpty({ a: 1 }); // true
|
|
@@ -2138,30 +2504,36 @@ const RecIs = {
|
|
|
2138
2504
|
/**
|
|
2139
2505
|
* Transforms each value in a record.
|
|
2140
2506
|
*
|
|
2507
|
+
* @see {@link mapWithKey} for transforming values with key access.
|
|
2508
|
+
* @see {@link mapKeys} for transforming keys while preserving values.
|
|
2509
|
+
* @see {@link mapEntries} for transforming keys and values simultaneously.
|
|
2510
|
+
*
|
|
2141
2511
|
* @example
|
|
2142
2512
|
* ```ts
|
|
2143
2513
|
* pipe({ a: 1, b: 2 }, Rec.map(n => n * 2)); // { a: 2, b: 4 }
|
|
2144
2514
|
* ```
|
|
2145
2515
|
*/
|
|
2146
|
-
const map$1 = (
|
|
2147
|
-
const recordKeys = Object.keys(
|
|
2148
|
-
const recordValues = Object.values(
|
|
2149
|
-
const result = Object.create(Object.getPrototypeOf(
|
|
2516
|
+
const map$1 = (transform) => (record) => {
|
|
2517
|
+
const recordKeys = Object.keys(record);
|
|
2518
|
+
const recordValues = Object.values(record);
|
|
2519
|
+
const result = Object.create(Object.getPrototypeOf(record));
|
|
2150
2520
|
for (let i = 0; i < recordKeys.length; i++) {
|
|
2151
2521
|
const key = recordKeys[i];
|
|
2152
2522
|
if (key === "__proto__") Object.defineProperty(result, "__proto__", {
|
|
2153
|
-
value:
|
|
2523
|
+
value: transform(recordValues[i]),
|
|
2154
2524
|
writable: true,
|
|
2155
2525
|
enumerable: true,
|
|
2156
2526
|
configurable: true
|
|
2157
2527
|
});
|
|
2158
|
-
else result[key] =
|
|
2528
|
+
else result[key] = transform(recordValues[i]);
|
|
2159
2529
|
}
|
|
2160
2530
|
return result;
|
|
2161
2531
|
};
|
|
2162
2532
|
/**
|
|
2163
2533
|
* Maps each value in a record with a function returning a `Maybe`, keeping only `Some` values.
|
|
2164
2534
|
*
|
|
2535
|
+
* @see {@link filter} for filtering with a boolean predicate.
|
|
2536
|
+
*
|
|
2165
2537
|
* @example
|
|
2166
2538
|
* ```ts
|
|
2167
2539
|
* pipe(
|
|
@@ -2170,12 +2542,12 @@ const map$1 = (f) => (data) => {
|
|
|
2170
2542
|
* ); // { b: 20 }
|
|
2171
2543
|
* ```
|
|
2172
2544
|
*/
|
|
2173
|
-
const filterMap$1 = (
|
|
2174
|
-
const recordKeys = Object.keys(
|
|
2175
|
-
const recordValues = Object.values(
|
|
2176
|
-
const result = Object.create(Object.getPrototypeOf(
|
|
2545
|
+
const filterMap$1 = (transform) => (record) => {
|
|
2546
|
+
const recordKeys = Object.keys(record);
|
|
2547
|
+
const recordValues = Object.values(record);
|
|
2548
|
+
const result = Object.create(Object.getPrototypeOf(record));
|
|
2177
2549
|
for (let i = 0; i < recordKeys.length; i++) {
|
|
2178
|
-
const maybeVal =
|
|
2550
|
+
const maybeVal = transform(recordValues[i]);
|
|
2179
2551
|
if (maybeVal.kind === "Some") _setKey(result, recordKeys[i], maybeVal.value);
|
|
2180
2552
|
}
|
|
2181
2553
|
return result;
|
|
@@ -2183,49 +2555,56 @@ const filterMap$1 = (f) => (data) => {
|
|
|
2183
2555
|
/**
|
|
2184
2556
|
* Transforms each value in a record, also receiving the key.
|
|
2185
2557
|
*
|
|
2558
|
+
* @see {@link map} for transforming values without key access.
|
|
2559
|
+
*
|
|
2186
2560
|
* @example
|
|
2187
2561
|
* ```ts
|
|
2188
2562
|
* pipe({ a: 1, b: 2 }, Rec.mapWithKey((k, v) => `${k}:${v}`));
|
|
2189
2563
|
* // { a: "a:1", b: "b:2" }
|
|
2190
2564
|
* ```
|
|
2191
2565
|
*/
|
|
2192
|
-
const mapWithKey = (
|
|
2193
|
-
const recordKeys = Object.keys(
|
|
2194
|
-
const recordValues = Object.values(
|
|
2195
|
-
const result = Object.create(Object.getPrototypeOf(
|
|
2566
|
+
const mapWithKey = (transform) => (record) => {
|
|
2567
|
+
const recordKeys = Object.keys(record);
|
|
2568
|
+
const recordValues = Object.values(record);
|
|
2569
|
+
const result = Object.create(Object.getPrototypeOf(record));
|
|
2196
2570
|
for (let i = 0; i < recordKeys.length; i++) {
|
|
2197
2571
|
const key = recordKeys[i];
|
|
2198
|
-
_setKey(result, key,
|
|
2572
|
+
_setKey(result, key, transform(key, recordValues[i]));
|
|
2199
2573
|
}
|
|
2200
2574
|
return result;
|
|
2201
2575
|
};
|
|
2202
2576
|
/**
|
|
2203
2577
|
* Filters values in a record by a predicate.
|
|
2204
2578
|
*
|
|
2579
|
+
* @see {@link filterWithKey} for filtering values with key access.
|
|
2580
|
+
* @see {@link filterMap} for filtering and mapping in a single pass.
|
|
2581
|
+
*
|
|
2205
2582
|
* @example
|
|
2206
2583
|
* ```ts
|
|
2207
2584
|
* pipe({ a: 1, b: 2, c: 3 }, Rec.filter(n => n > 1)); // { b: 2, c: 3 }
|
|
2208
2585
|
* ```
|
|
2209
2586
|
*/
|
|
2210
|
-
const filter$1 = (predicate) => (
|
|
2211
|
-
const recordKeys = Object.keys(
|
|
2212
|
-
const recordValues = Object.values(
|
|
2213
|
-
const result = Object.create(Object.getPrototypeOf(
|
|
2587
|
+
const filter$1 = (predicate) => (record) => {
|
|
2588
|
+
const recordKeys = Object.keys(record);
|
|
2589
|
+
const recordValues = Object.values(record);
|
|
2590
|
+
const result = Object.create(Object.getPrototypeOf(record));
|
|
2214
2591
|
for (let i = 0; i < recordKeys.length; i++) if (predicate(recordValues[i])) _setKey(result, recordKeys[i], recordValues[i]);
|
|
2215
2592
|
return result;
|
|
2216
2593
|
};
|
|
2217
2594
|
/**
|
|
2218
2595
|
* Filters values in a record by a predicate that also receives the key.
|
|
2219
2596
|
*
|
|
2597
|
+
* @see {@link filter} for filtering values without key access.
|
|
2598
|
+
*
|
|
2220
2599
|
* @example
|
|
2221
2600
|
* ```ts
|
|
2222
2601
|
* pipe({ a: 1, b: 2, c: 3 }, Rec.filterWithKey((k, v) => k !== "a" && v > 0));
|
|
2223
2602
|
* // { b: 2 }
|
|
2224
2603
|
* ```
|
|
2225
2604
|
*/
|
|
2226
|
-
const filterWithKey = (predicate) => (
|
|
2605
|
+
const filterWithKey = (predicate) => (record) => {
|
|
2227
2606
|
const result = {};
|
|
2228
|
-
for (const [k, v] of Object.entries(
|
|
2607
|
+
for (const [k, v] of Object.entries(record)) if (predicate(k, v)) _setKey(result, k, v);
|
|
2229
2608
|
return result;
|
|
2230
2609
|
};
|
|
2231
2610
|
/**
|
|
@@ -2237,37 +2616,46 @@ const filterWithKey = (predicate) => (data) => {
|
|
|
2237
2616
|
* pipe({ a: 1, b: 2 }, Rec.lookup("c")); // None
|
|
2238
2617
|
* ```
|
|
2239
2618
|
*/
|
|
2240
|
-
const lookup = (key) => (
|
|
2619
|
+
const lookup = (key) => (record) => Object.hasOwn(record, key) ? {
|
|
2241
2620
|
kind: "Some",
|
|
2242
|
-
value:
|
|
2621
|
+
value: record[key]
|
|
2243
2622
|
} : { kind: "None" };
|
|
2244
2623
|
/**
|
|
2245
2624
|
* Returns all keys of a record.
|
|
2246
2625
|
*
|
|
2626
|
+
* @see {@link values} for extracting record values.
|
|
2627
|
+
* @see {@link entries} for extracting key-value pairs.
|
|
2628
|
+
*
|
|
2247
2629
|
* @example
|
|
2248
2630
|
* ```ts
|
|
2249
2631
|
* Rec.keys({ a: 1, b: 2 }); // ["a", "b"]
|
|
2250
2632
|
* ```
|
|
2251
2633
|
*/
|
|
2252
|
-
const keys = (
|
|
2634
|
+
const keys = (record) => Object.keys(record);
|
|
2253
2635
|
/**
|
|
2254
2636
|
* Returns all values of a record.
|
|
2255
2637
|
*
|
|
2638
|
+
* @see {@link keys} for extracting record keys.
|
|
2639
|
+
* @see {@link entries} for extracting key-value pairs.
|
|
2640
|
+
*
|
|
2256
2641
|
* @example
|
|
2257
2642
|
* ```ts
|
|
2258
2643
|
* Rec.values({ a: 1, b: 2 }); // [1, 2]
|
|
2259
2644
|
* ```
|
|
2260
2645
|
*/
|
|
2261
|
-
const values = (
|
|
2646
|
+
const values = (record) => Object.values(record);
|
|
2262
2647
|
/**
|
|
2263
2648
|
* Returns all key-value pairs of a record.
|
|
2264
2649
|
*
|
|
2650
|
+
* @see {@link keys} for extracting record keys.
|
|
2651
|
+
* @see {@link values} for extracting record values.
|
|
2652
|
+
*
|
|
2265
2653
|
* @example
|
|
2266
2654
|
* ```ts
|
|
2267
2655
|
* Rec.entries({ a: 1, b: 2 }); // [["a", 1], ["b", 2]]
|
|
2268
2656
|
* ```
|
|
2269
2657
|
*/
|
|
2270
|
-
const entries = (
|
|
2658
|
+
const entries = (record) => Object.entries(record);
|
|
2271
2659
|
const RecFrom = {
|
|
2272
2660
|
/**
|
|
2273
2661
|
* Creates a record from key-value pairs.
|
|
@@ -2277,9 +2665,9 @@ const RecFrom = {
|
|
|
2277
2665
|
* Rec.from.entries([["a", 1], ["b", 2]]); // { a: 1, b: 2 }
|
|
2278
2666
|
* ```
|
|
2279
2667
|
*/
|
|
2280
|
-
entries: (
|
|
2668
|
+
entries: (pairs) => Object.fromEntries(pairs) };
|
|
2281
2669
|
/**
|
|
2282
|
-
* Groups elements of an array into a record keyed by the result of `
|
|
2670
|
+
* Groups elements of an array into a record keyed by the result of `keySelector`. Each key maps to
|
|
2283
2671
|
* the array of elements that produced it, in insertion order.
|
|
2284
2672
|
*
|
|
2285
2673
|
* Unlike `Dict.groupBy`, keys are always strings. Use `Dict.groupBy` when you need non-string
|
|
@@ -2293,10 +2681,10 @@ entries: (data) => Object.fromEntries(data) };
|
|
|
2293
2681
|
* ); // { a: ["apple", "avocado"], b: ["banana", "blueberry"] }
|
|
2294
2682
|
* ```
|
|
2295
2683
|
*/
|
|
2296
|
-
const groupBy = (
|
|
2684
|
+
const groupBy = (keySelector) => (items) => {
|
|
2297
2685
|
const result = {};
|
|
2298
2686
|
for (const item of items) {
|
|
2299
|
-
const key =
|
|
2687
|
+
const key = keySelector(item);
|
|
2300
2688
|
if (Object.hasOwn(result, key)) result[key].push(item);
|
|
2301
2689
|
else _setKey(result, key, [item]);
|
|
2302
2690
|
}
|
|
@@ -2305,40 +2693,46 @@ const groupBy = (keyFn) => (items) => {
|
|
|
2305
2693
|
/**
|
|
2306
2694
|
* Picks specific keys from a record.
|
|
2307
2695
|
*
|
|
2696
|
+
* @see {@link omit} for removing specific keys from a record.
|
|
2697
|
+
*
|
|
2308
2698
|
* @example
|
|
2309
2699
|
* ```ts
|
|
2310
2700
|
* pipe({ a: 1, b: 2, c: 3 }, Rec.pick("a", "c")); // { a: 1, c: 3 }
|
|
2311
2701
|
* ```
|
|
2312
2702
|
*/
|
|
2313
|
-
const pick = (...pickedKeys) => (
|
|
2703
|
+
const pick = (...pickedKeys) => (record) => {
|
|
2314
2704
|
const result = {};
|
|
2315
|
-
for (const key of pickedKeys) if (Object.hasOwn(
|
|
2705
|
+
for (const key of pickedKeys) if (Object.hasOwn(record, key)) _setKey(result, key, record[key]);
|
|
2316
2706
|
return result;
|
|
2317
2707
|
};
|
|
2318
2708
|
/**
|
|
2319
2709
|
* Omits specific keys from a record.
|
|
2320
2710
|
*
|
|
2711
|
+
* @see {@link pick} for retaining only specific keys from a record.
|
|
2712
|
+
*
|
|
2321
2713
|
* @example
|
|
2322
2714
|
* ```ts
|
|
2323
2715
|
* pipe({ a: 1, b: 2, c: 3 }, Rec.omit("b")); // { a: 1, c: 3 }
|
|
2324
2716
|
* ```
|
|
2325
2717
|
*/
|
|
2326
|
-
const omit = (...omittedKeys) => (
|
|
2718
|
+
const omit = (...omittedKeys) => (record) => {
|
|
2327
2719
|
const omitSet = new Set(omittedKeys);
|
|
2328
2720
|
const result = {};
|
|
2329
|
-
for (const key of Object.keys(
|
|
2721
|
+
for (const key of Object.keys(record)) if (!omitSet.has(key)) _setKey(result, key, record[key]);
|
|
2330
2722
|
return result;
|
|
2331
2723
|
};
|
|
2332
2724
|
/**
|
|
2333
2725
|
* Merges two records. Values from the second record take precedence.
|
|
2334
2726
|
*
|
|
2727
|
+
* @see {@link mergeWith} for merging records with custom collision resolution.
|
|
2728
|
+
*
|
|
2335
2729
|
* @example
|
|
2336
2730
|
* ```ts
|
|
2337
2731
|
* pipe({ a: 1, b: 2 }, Rec.merge({ b: 3, c: 4 })); // { a: 1, b: 3, c: 4 }
|
|
2338
2732
|
* ```
|
|
2339
2733
|
*/
|
|
2340
|
-
const merge = (other) => (
|
|
2341
|
-
...
|
|
2734
|
+
const merge = (other) => (record) => ({
|
|
2735
|
+
...record,
|
|
2342
2736
|
...other
|
|
2343
2737
|
});
|
|
2344
2738
|
function mergeWith(combine) {
|
|
@@ -2368,24 +2762,27 @@ function mergeWith(combine) {
|
|
|
2368
2762
|
* Rec.size({ a: 1, b: 2 }); // 2
|
|
2369
2763
|
* ```
|
|
2370
2764
|
*/
|
|
2371
|
-
const size$1 = (
|
|
2765
|
+
const size$1 = (record) => Object.keys(record).length;
|
|
2372
2766
|
/**
|
|
2373
2767
|
* Transforms each key while preserving values.
|
|
2374
2768
|
* If two keys map to the same new key, the last one wins.
|
|
2375
2769
|
*
|
|
2770
|
+
* @see {@link map} for transforming values.
|
|
2771
|
+
* @see {@link mapEntries} for transforming keys and values simultaneously.
|
|
2772
|
+
*
|
|
2376
2773
|
* @example
|
|
2377
2774
|
* ```ts
|
|
2378
2775
|
* pipe({ firstName: "Alice", lastName: "Smith" }, Rec.mapKeys(k => k.toUpperCase()));
|
|
2379
2776
|
* // { FIRSTNAME: "Alice", LASTNAME: "Smith" }
|
|
2380
2777
|
* ```
|
|
2381
2778
|
*/
|
|
2382
|
-
const mapKeys = (
|
|
2383
|
-
const recKeys = Object.keys(
|
|
2384
|
-
if (recKeys.length === 0) return
|
|
2779
|
+
const mapKeys = (transform) => (record) => {
|
|
2780
|
+
const recKeys = Object.keys(record);
|
|
2781
|
+
if (recKeys.length === 0) return record;
|
|
2385
2782
|
const result = {};
|
|
2386
2783
|
for (let i = 0; i < recKeys.length; i++) {
|
|
2387
2784
|
const k = recKeys[i];
|
|
2388
|
-
_setKey(result,
|
|
2785
|
+
_setKey(result, transform(k), record[k]);
|
|
2389
2786
|
}
|
|
2390
2787
|
return result;
|
|
2391
2788
|
};
|
|
@@ -2399,13 +2796,13 @@ const mapKeys = (f) => (data) => {
|
|
|
2399
2796
|
* // { a: 1, c: 3 }
|
|
2400
2797
|
* ```
|
|
2401
2798
|
*/
|
|
2402
|
-
const compact = (
|
|
2403
|
-
const recKeys = Object.keys(
|
|
2799
|
+
const compact = (record) => {
|
|
2800
|
+
const recKeys = Object.keys(record);
|
|
2404
2801
|
if (recKeys.length === 0) return {};
|
|
2405
2802
|
const result = {};
|
|
2406
2803
|
for (let i = 0; i < recKeys.length; i++) {
|
|
2407
2804
|
const k = recKeys[i];
|
|
2408
|
-
const v =
|
|
2805
|
+
const v = record[k];
|
|
2409
2806
|
if (v.kind === "Some") _setKey(result, k, v.value);
|
|
2410
2807
|
}
|
|
2411
2808
|
return result;
|
|
@@ -2413,6 +2810,9 @@ const compact = (data) => {
|
|
|
2413
2810
|
/**
|
|
2414
2811
|
* Transforms key and value pairs simultaneously.
|
|
2415
2812
|
*
|
|
2813
|
+
* @see {@link map} for transforming values.
|
|
2814
|
+
* @see {@link mapKeys} for transforming keys.
|
|
2815
|
+
*
|
|
2416
2816
|
* @example
|
|
2417
2817
|
* ```ts
|
|
2418
2818
|
* pipe(
|
|
@@ -2421,13 +2821,13 @@ const compact = (data) => {
|
|
|
2421
2821
|
* ); // { A: 10, B: 20 }
|
|
2422
2822
|
* ```
|
|
2423
2823
|
*/
|
|
2424
|
-
const mapEntries = (
|
|
2425
|
-
const recKeys = Object.keys(
|
|
2824
|
+
const mapEntries = (transform) => (record) => {
|
|
2825
|
+
const recKeys = Object.keys(record);
|
|
2426
2826
|
if (recKeys.length === 0) return {};
|
|
2427
2827
|
const result = {};
|
|
2428
2828
|
for (let i = 0; i < recKeys.length; i++) {
|
|
2429
2829
|
const k = recKeys[i];
|
|
2430
|
-
const [newKey, newVal] =
|
|
2830
|
+
const [newKey, newVal] = transform(k, record[k]);
|
|
2431
2831
|
_setKey(result, newKey, newVal);
|
|
2432
2832
|
}
|
|
2433
2833
|
return result;
|
|
@@ -2443,12 +2843,12 @@ const mapEntries = (f) => (data) => {
|
|
|
2443
2843
|
* ); // { user: { profile: { age: 31 } } }
|
|
2444
2844
|
* ```
|
|
2445
2845
|
*/
|
|
2446
|
-
const updateIn = (path,
|
|
2846
|
+
const updateIn = (path, transform) => (record) => {
|
|
2447
2847
|
const updateNode = (obj, pathKeys) => {
|
|
2448
2848
|
const [head, ...tail] = pathKeys;
|
|
2449
2849
|
if (tail.length === 0) return {
|
|
2450
2850
|
...obj,
|
|
2451
|
-
[head]:
|
|
2851
|
+
[head]: transform(obj?.[head])
|
|
2452
2852
|
};
|
|
2453
2853
|
const child = obj && typeof obj === "object" && head in obj ? obj[head] : {};
|
|
2454
2854
|
return {
|
|
@@ -2456,12 +2856,12 @@ const updateIn = (path, f) => (data) => {
|
|
|
2456
2856
|
[head]: updateNode(child, tail)
|
|
2457
2857
|
};
|
|
2458
2858
|
};
|
|
2459
|
-
return updateNode(
|
|
2859
|
+
return updateNode(record, path);
|
|
2460
2860
|
};
|
|
2461
2861
|
const Rec = {
|
|
2462
2862
|
is: RecIs,
|
|
2463
2863
|
from: RecFrom,
|
|
2464
|
-
to: { Dict: (
|
|
2864
|
+
to: { Dict: (record) => new globalThis.Map(Object.entries(record)) },
|
|
2465
2865
|
map: map$1,
|
|
2466
2866
|
filterMap: filterMap$1,
|
|
2467
2867
|
mapWithKey,
|
|
@@ -2491,13 +2891,13 @@ const Rec = {
|
|
|
2491
2891
|
},
|
|
2492
2892
|
NonEmpty: {
|
|
2493
2893
|
singleton: (key, value) => ({ [key]: value }),
|
|
2494
|
-
from: {
|
|
2495
|
-
keys: (
|
|
2496
|
-
values: (
|
|
2497
|
-
entries: (
|
|
2498
|
-
reduce: (
|
|
2499
|
-
map: (
|
|
2500
|
-
mapWithKey: (
|
|
2894
|
+
from: { record: (record) => _isNonEmpty(record) ? Maybe.make.some(record) : Maybe.make.none() },
|
|
2895
|
+
keys: (record) => keys(record),
|
|
2896
|
+
values: (record) => values(record),
|
|
2897
|
+
entries: (record) => entries(record),
|
|
2898
|
+
reduce: (reducer) => (record) => values(record).reduce(reducer),
|
|
2899
|
+
map: (transform) => (record) => map$1(transform)(record),
|
|
2900
|
+
mapWithKey: (transform) => (record) => mapWithKey(transform)(record)
|
|
2501
2901
|
}
|
|
2502
2902
|
};
|
|
2503
2903
|
//#endregion
|
|
@@ -2508,18 +2908,21 @@ const StrNonEmptyConst = { from: {
|
|
|
2508
2908
|
*
|
|
2509
2909
|
* @example
|
|
2510
2910
|
* ```ts
|
|
2511
|
-
* Str.NonEmpty.from.
|
|
2512
|
-
* Str.NonEmpty.from.
|
|
2911
|
+
* Str.NonEmpty.from.string("hello"); // Some("hello")
|
|
2912
|
+
* Str.NonEmpty.from.string(""); // None
|
|
2513
2913
|
* ```
|
|
2514
2914
|
*/
|
|
2515
|
-
|
|
2516
|
-
const isEmpty$1 = (
|
|
2517
|
-
const isNonEmpty$1 = (
|
|
2915
|
+
string: (text) => text.length > 0 ? Maybe.make.some(text) : Maybe.make.none() } };
|
|
2916
|
+
const isEmpty$1 = (text) => text.length === 0;
|
|
2917
|
+
const isNonEmpty$1 = (text) => text.length > 0;
|
|
2918
|
+
const isBlank = (text) => text.trim().length === 0;
|
|
2518
2919
|
const Str = {
|
|
2519
2920
|
is: {
|
|
2520
2921
|
/**
|
|
2521
2922
|
* Returns `true` when the string is empty.
|
|
2522
2923
|
*
|
|
2924
|
+
* @see {@link nonEmpty} for checking if a string contains at least one character.
|
|
2925
|
+
*
|
|
2523
2926
|
* @example
|
|
2524
2927
|
* ```ts
|
|
2525
2928
|
* pipe("", Str.is.empty); // true
|
|
@@ -2529,8 +2932,22 @@ const Str = {
|
|
|
2529
2932
|
empty: isEmpty$1,
|
|
2530
2933
|
/**
|
|
2531
2934
|
* Type guard to check if a string is non-empty.
|
|
2935
|
+
*
|
|
2936
|
+
* @see {@link empty} for checking if a string has zero length.
|
|
2532
2937
|
*/
|
|
2533
|
-
nonEmpty: isNonEmpty$1
|
|
2938
|
+
nonEmpty: isNonEmpty$1,
|
|
2939
|
+
/**
|
|
2940
|
+
* Returns `true` when the string is empty or contains only whitespace.
|
|
2941
|
+
*
|
|
2942
|
+
* @see {@link empty} for checking zero length without trimming whitespace.
|
|
2943
|
+
*
|
|
2944
|
+
* @example
|
|
2945
|
+
* ```ts
|
|
2946
|
+
* pipe(" ", Str.is.blank); // true
|
|
2947
|
+
* pipe("hi", Str.is.blank); // false
|
|
2948
|
+
* ```
|
|
2949
|
+
*/
|
|
2950
|
+
blank: isBlank
|
|
2534
2951
|
},
|
|
2535
2952
|
/**
|
|
2536
2953
|
* Splits a string by a separator. Data-last: use in `pipe`.
|
|
@@ -2540,7 +2957,7 @@ const Str = {
|
|
|
2540
2957
|
* pipe("a,b,c", Str.split(",")); // ["a", "b", "c"]
|
|
2541
2958
|
* ```
|
|
2542
2959
|
*/
|
|
2543
|
-
split: (separator) => (
|
|
2960
|
+
split: (separator) => (text) => text.split(separator),
|
|
2544
2961
|
/**
|
|
2545
2962
|
* Removes leading and trailing whitespace from a string.
|
|
2546
2963
|
*
|
|
@@ -2549,7 +2966,7 @@ const Str = {
|
|
|
2549
2966
|
* pipe(" hello ", Str.trim); // "hello"
|
|
2550
2967
|
* ```
|
|
2551
2968
|
*/
|
|
2552
|
-
trim: (
|
|
2969
|
+
trim: (text) => text.trim(),
|
|
2553
2970
|
/**
|
|
2554
2971
|
* Returns `true` when the string contains the given substring.
|
|
2555
2972
|
*
|
|
@@ -2559,74 +2976,88 @@ const Str = {
|
|
|
2559
2976
|
* pipe("hello world", Str.includes("xyz")); // false
|
|
2560
2977
|
* ```
|
|
2561
2978
|
*/
|
|
2562
|
-
includes: (substring) => (
|
|
2979
|
+
includes: (substring) => (text) => text.includes(substring),
|
|
2563
2980
|
/**
|
|
2564
2981
|
* Replaces the first occurrence of a pattern in a string. Data-last: use in `pipe`.
|
|
2565
2982
|
*
|
|
2983
|
+
* @see {@link replaceAll} for substituting every occurrence instead of only the first.
|
|
2984
|
+
*
|
|
2566
2985
|
* @example
|
|
2567
2986
|
* ```ts
|
|
2568
2987
|
* pipe("foo foo foo", Str.replace("foo", "bar")); // "bar foo foo"
|
|
2569
2988
|
* pipe("Hello World", Str.replace(/world/i, "Earth")); // "Hello Earth"
|
|
2570
2989
|
* ```
|
|
2571
2990
|
*/
|
|
2572
|
-
replace: (pattern, replacement) => (
|
|
2991
|
+
replace: (pattern, replacement) => (text) => text.replace(pattern, replacement),
|
|
2573
2992
|
/**
|
|
2574
2993
|
* Replaces all occurrences of a pattern in a string. Data-last: use in `pipe`.
|
|
2575
2994
|
*
|
|
2995
|
+
* @see {@link replace} for substituting only the first occurrence.
|
|
2996
|
+
*
|
|
2576
2997
|
* @example
|
|
2577
2998
|
* ```ts
|
|
2578
2999
|
* pipe("foo foo foo", Str.replaceAll("foo", "bar")); // "bar bar bar"
|
|
2579
3000
|
* pipe("aAbBaA", Str.replaceAll(/a/gi, "x")); // "xxBBxx"
|
|
2580
3001
|
* ```
|
|
2581
3002
|
*/
|
|
2582
|
-
replaceAll: (pattern, replacement) => (
|
|
3003
|
+
replaceAll: (pattern, replacement) => (text) => text.replaceAll(pattern, replacement),
|
|
2583
3004
|
/**
|
|
2584
3005
|
* Returns `true` when the string starts with the given prefix.
|
|
2585
3006
|
*
|
|
3007
|
+
* @see {@link endsWith} for checking suffix matches.
|
|
3008
|
+
*
|
|
2586
3009
|
* @example
|
|
2587
3010
|
* ```ts
|
|
2588
3011
|
* pipe("hello world", Str.startsWith("hello")); // true
|
|
2589
3012
|
* pipe("hello world", Str.startsWith("world")); // false
|
|
2590
3013
|
* ```
|
|
2591
3014
|
*/
|
|
2592
|
-
startsWith: (prefix) => (
|
|
3015
|
+
startsWith: (prefix) => (text) => text.startsWith(prefix),
|
|
2593
3016
|
/**
|
|
2594
3017
|
* Returns `true` when the string ends with the given suffix.
|
|
2595
3018
|
*
|
|
3019
|
+
* @see {@link startsWith} for checking prefix matches.
|
|
3020
|
+
*
|
|
2596
3021
|
* @example
|
|
2597
3022
|
* ```ts
|
|
2598
3023
|
* pipe("hello world", Str.endsWith("world")); // true
|
|
2599
3024
|
* pipe("hello world", Str.endsWith("hello")); // false
|
|
2600
3025
|
* ```
|
|
2601
3026
|
*/
|
|
2602
|
-
endsWith: (suffix) => (
|
|
3027
|
+
endsWith: (suffix) => (text) => text.endsWith(suffix),
|
|
2603
3028
|
/**
|
|
2604
3029
|
* Converts a string to uppercase.
|
|
2605
3030
|
*
|
|
3031
|
+
* @see {@link toLowerCase} for lowercasing characters.
|
|
3032
|
+
*
|
|
2606
3033
|
* @example
|
|
2607
3034
|
* ```ts
|
|
2608
3035
|
* pipe("hello", Str.toUpperCase); // "HELLO"
|
|
2609
3036
|
* ```
|
|
2610
3037
|
*/
|
|
2611
|
-
toUpperCase: (
|
|
3038
|
+
toUpperCase: (text) => text.toUpperCase(),
|
|
2612
3039
|
/**
|
|
2613
3040
|
* Converts a string to lowercase.
|
|
2614
3041
|
*
|
|
3042
|
+
* @see {@link toUpperCase} for uppercasing characters.
|
|
3043
|
+
*
|
|
2615
3044
|
* @example
|
|
2616
3045
|
* ```ts
|
|
2617
3046
|
* pipe("HELLO", Str.toLowerCase); // "hello"
|
|
2618
3047
|
* ```
|
|
2619
3048
|
*/
|
|
2620
|
-
toLowerCase: (
|
|
3049
|
+
toLowerCase: (text) => text.toLowerCase(),
|
|
2621
3050
|
/**
|
|
2622
3051
|
* Converts the first character of a string to uppercase.
|
|
2623
3052
|
*
|
|
3053
|
+
* @see {@link uncapitalize} for converting the first character to lowercase.
|
|
3054
|
+
*
|
|
2624
3055
|
* @example
|
|
2625
3056
|
* ```ts
|
|
2626
3057
|
* pipe("hello", Str.capitalize); // "Hello"
|
|
2627
3058
|
* ```
|
|
2628
3059
|
*/
|
|
2629
|
-
capitalize: (
|
|
3060
|
+
capitalize: (text) => text.length === 0 ? "" : text.charAt(0).toUpperCase() + text.slice(1),
|
|
2630
3061
|
/**
|
|
2631
3062
|
* Splits a string into lines, normalising `\r\n` and `\r` line endings.
|
|
2632
3063
|
*
|
|
@@ -2636,7 +3067,7 @@ const Str = {
|
|
|
2636
3067
|
* Str.lines("a\r\nb"); // ["a", "b"]
|
|
2637
3068
|
* ```
|
|
2638
3069
|
*/
|
|
2639
|
-
lines: (
|
|
3070
|
+
lines: (text) => text.split(/\r?\n|\r/),
|
|
2640
3071
|
/**
|
|
2641
3072
|
* Splits a string into words on any whitespace boundary, filtering out empty strings.
|
|
2642
3073
|
*
|
|
@@ -2645,17 +3076,7 @@ const Str = {
|
|
|
2645
3076
|
* Str.words(" hello world "); // ["hello", "world"]
|
|
2646
3077
|
* ```
|
|
2647
3078
|
*/
|
|
2648
|
-
words: (
|
|
2649
|
-
/**
|
|
2650
|
-
* Returns `true` when the string is empty or contains only whitespace.
|
|
2651
|
-
*
|
|
2652
|
-
* @example
|
|
2653
|
-
* ```ts
|
|
2654
|
-
* pipe(" ", Str.isBlank); // true
|
|
2655
|
-
* pipe("hi", Str.isBlank); // false
|
|
2656
|
-
* ```
|
|
2657
|
-
*/
|
|
2658
|
-
isBlank: (s) => s.trim().length === 0,
|
|
3079
|
+
words: (text) => text.trim().split(/\s+/).filter(Boolean),
|
|
2659
3080
|
/**
|
|
2660
3081
|
* Returns the length of the string.
|
|
2661
3082
|
*
|
|
@@ -2665,7 +3086,7 @@ const Str = {
|
|
|
2665
3086
|
* pipe("", Str.length); // 0
|
|
2666
3087
|
* ```
|
|
2667
3088
|
*/
|
|
2668
|
-
length: (
|
|
3089
|
+
length: (text) => text.length,
|
|
2669
3090
|
/**
|
|
2670
3091
|
* Extracts a substring between two indices. Data-last: use in `pipe`.
|
|
2671
3092
|
*
|
|
@@ -2675,27 +3096,31 @@ const Str = {
|
|
|
2675
3096
|
* pipe("hello", Str.slice(2)); // "llo"
|
|
2676
3097
|
* ```
|
|
2677
3098
|
*/
|
|
2678
|
-
slice: (start, end) => (
|
|
3099
|
+
slice: (start, end) => (text) => text.slice(start, end),
|
|
2679
3100
|
/**
|
|
2680
3101
|
* Pads the start of a string to a specified length. Data-last: use in `pipe`.
|
|
2681
3102
|
*
|
|
3103
|
+
* @see {@link padEnd} for padding the right side of a string.
|
|
3104
|
+
*
|
|
2682
3105
|
* @example
|
|
2683
3106
|
* ```ts
|
|
2684
3107
|
* pipe("5", Str.padStart(3, "0")); // "005"
|
|
2685
3108
|
* pipe("hi", Str.padStart(5)); // " hi"
|
|
2686
3109
|
* ```
|
|
2687
3110
|
*/
|
|
2688
|
-
padStart: (maxLength, fillString) => (
|
|
3111
|
+
padStart: (maxLength, fillString) => (text) => text.padStart(maxLength, fillString),
|
|
2689
3112
|
/**
|
|
2690
3113
|
* Pads the end of a string to a specified length. Data-last: use in `pipe`.
|
|
2691
3114
|
*
|
|
3115
|
+
* @see {@link padStart} for padding the left side of a string.
|
|
3116
|
+
*
|
|
2692
3117
|
* @example
|
|
2693
3118
|
* ```ts
|
|
2694
3119
|
* pipe("hi", Str.padEnd(5, ".")); // "hi..."
|
|
2695
3120
|
* pipe("hi", Str.padEnd(5)); // "hi "
|
|
2696
3121
|
* ```
|
|
2697
3122
|
*/
|
|
2698
|
-
padEnd: (maxLength, fillString) => (
|
|
3123
|
+
padEnd: (maxLength, fillString) => (text) => text.padEnd(maxLength, fillString),
|
|
2699
3124
|
/**
|
|
2700
3125
|
* Safe number parsers that return `Maybe` instead of `NaN`.
|
|
2701
3126
|
*/
|
|
@@ -2703,6 +3128,8 @@ const Str = {
|
|
|
2703
3128
|
/**
|
|
2704
3129
|
* Parses a string as an integer (base 10). Returns `None` if the result is `NaN`.
|
|
2705
3130
|
*
|
|
3131
|
+
* @see {@link float} for parsing floating-point numbers.
|
|
3132
|
+
*
|
|
2706
3133
|
* @example
|
|
2707
3134
|
* ```ts
|
|
2708
3135
|
* Str.parse.int("42"); // Some(42)
|
|
@@ -2710,14 +3137,16 @@ const Str = {
|
|
|
2710
3137
|
* Str.parse.int("abc"); // None
|
|
2711
3138
|
* ```
|
|
2712
3139
|
*/
|
|
2713
|
-
int: (
|
|
2714
|
-
if (
|
|
2715
|
-
const n = Number.parseInt(
|
|
3140
|
+
int: (text) => {
|
|
3141
|
+
if (text.length === 0) return Maybe.make.none();
|
|
3142
|
+
const n = Number.parseInt(text, 10);
|
|
2716
3143
|
return Number.isNaN(n) ? Maybe.make.none() : Maybe.make.some(n);
|
|
2717
3144
|
},
|
|
2718
3145
|
/**
|
|
2719
3146
|
* Parses a string as a floating-point number. Returns `None` if the result is `NaN`.
|
|
2720
3147
|
*
|
|
3148
|
+
* @see {@link int} for parsing base-10 integers.
|
|
3149
|
+
*
|
|
2721
3150
|
* @example
|
|
2722
3151
|
* ```ts
|
|
2723
3152
|
* Str.parse.float("3.14"); // Some(3.14)
|
|
@@ -2725,38 +3154,57 @@ const Str = {
|
|
|
2725
3154
|
* Str.parse.float("abc"); // None
|
|
2726
3155
|
* ```
|
|
2727
3156
|
*/
|
|
2728
|
-
float: (
|
|
2729
|
-
if (
|
|
2730
|
-
const n = Number.parseFloat(
|
|
3157
|
+
float: (text) => {
|
|
3158
|
+
if (text.length === 0) return Maybe.make.none();
|
|
3159
|
+
const n = Number.parseFloat(text);
|
|
2731
3160
|
return Number.isNaN(n) ? Maybe.make.none() : Maybe.make.some(n);
|
|
2732
3161
|
}
|
|
2733
3162
|
},
|
|
2734
3163
|
/**
|
|
2735
|
-
*
|
|
3164
|
+
* Matches a string against a regular expression.
|
|
3165
|
+
* Pure and safe: resets `pattern.lastIndex = 0` to prevent bugs with stateful `/g` and `/y` regexes.
|
|
3166
|
+
*
|
|
3167
|
+
* @see {@link test} for boolean validation without extracting capture groups.
|
|
2736
3168
|
*
|
|
2737
3169
|
* @example
|
|
2738
3170
|
* ```ts
|
|
2739
|
-
*
|
|
2740
|
-
* Str.
|
|
3171
|
+
* pipe("hello 42", Str.match(/\d+/)); // Some(["42"])
|
|
3172
|
+
* pipe("hello", Str.match(/\d+/)); // None
|
|
2741
3173
|
* ```
|
|
2742
3174
|
*/
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
3175
|
+
match: (pattern) => (text) => {
|
|
3176
|
+
pattern.lastIndex = 0;
|
|
3177
|
+
const result = text.match(pattern);
|
|
3178
|
+
return result !== null ? Maybe.make.some(result) : Maybe.make.none();
|
|
3179
|
+
},
|
|
3180
|
+
/**
|
|
3181
|
+
* Tests whether a string matches a regular expression.
|
|
3182
|
+
* Pure and safe: resets `pattern.lastIndex = 0` to prevent bugs with stateful `/g` and `/y` regexes.
|
|
3183
|
+
*
|
|
3184
|
+
* @see {@link match} for extracting capture groups into a Maybe.
|
|
3185
|
+
*
|
|
3186
|
+
* @example
|
|
3187
|
+
* ```ts
|
|
3188
|
+
* pipe("user@example.com", Str.test(/^[^@]+@[^@]+$/)); // true
|
|
3189
|
+
* pipe("invalid-email", Str.test(/^[^@]+@[^@]+$/)); // false
|
|
3190
|
+
* ```
|
|
3191
|
+
*/
|
|
3192
|
+
test: (pattern) => (text) => {
|
|
3193
|
+
pattern.lastIndex = 0;
|
|
3194
|
+
return pattern.test(text);
|
|
2749
3195
|
},
|
|
2750
3196
|
/**
|
|
2751
3197
|
* Converts the first character of a string to lower case.
|
|
2752
3198
|
*
|
|
3199
|
+
* @see {@link capitalize} for converting the first character to uppercase.
|
|
3200
|
+
*
|
|
2753
3201
|
* @example
|
|
2754
3202
|
* ```ts
|
|
2755
|
-
*
|
|
2756
|
-
* Str.uncapitalize
|
|
3203
|
+
* pipe("Hello", Str.uncapitalize); // "hello"
|
|
3204
|
+
* pipe("", Str.uncapitalize); // ""
|
|
2757
3205
|
* ```
|
|
2758
3206
|
*/
|
|
2759
|
-
uncapitalize: (
|
|
3207
|
+
uncapitalize: (text) => text.length === 0 ? "" : text.charAt(0).toLowerCase() + text.slice(1),
|
|
2760
3208
|
/**
|
|
2761
3209
|
* Truncates a string to a maximum length, appending an optional suffix (default `"..."`).
|
|
2762
3210
|
* Data-last curried signature.
|
|
@@ -2768,100 +3216,158 @@ const Str = {
|
|
|
2768
3216
|
* pipe("Hello, world!", Str.truncate({ length: 8, suffix: "…" })); // "Hello, w…"
|
|
2769
3217
|
* ```
|
|
2770
3218
|
*/
|
|
2771
|
-
truncate: (options) => (
|
|
3219
|
+
truncate: (options) => (text) => {
|
|
2772
3220
|
const { length: targetLength, suffix = "..." } = options;
|
|
2773
|
-
if (
|
|
3221
|
+
if (text.length <= targetLength) return text;
|
|
2774
3222
|
if (targetLength <= suffix.length) return suffix.slice(0, targetLength);
|
|
2775
|
-
return
|
|
3223
|
+
return text.slice(0, targetLength - suffix.length) + suffix;
|
|
2776
3224
|
},
|
|
2777
3225
|
NonEmpty: StrNonEmptyConst
|
|
2778
3226
|
};
|
|
2779
3227
|
//#endregion
|
|
2780
3228
|
//#region src/Data/Uniq.ts
|
|
2781
|
-
const isEmpty = (
|
|
2782
|
-
const isNonEmpty = (
|
|
3229
|
+
const isEmpty = (set) => set.size === 0;
|
|
3230
|
+
const isNonEmpty = (set) => set.size > 0;
|
|
2783
3231
|
const empty = () => new globalThis.Set();
|
|
2784
3232
|
const singleton = (item) => new globalThis.Set([item]);
|
|
2785
|
-
const fromArray = (
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
3233
|
+
const fromArray = (items) => new globalThis.Set(items);
|
|
3234
|
+
/**
|
|
3235
|
+
* Returns `true` when the set contains the given element.
|
|
3236
|
+
*
|
|
3237
|
+
* @see {@link add} for inserting an element into a set.
|
|
3238
|
+
* @see {@link remove} for deleting an element from a set.
|
|
3239
|
+
*/
|
|
3240
|
+
const has = (item) => (set) => set.has(item);
|
|
3241
|
+
const size = (set) => set.size;
|
|
3242
|
+
/**
|
|
3243
|
+
* Returns a new set with the element added.
|
|
3244
|
+
*
|
|
3245
|
+
* @see {@link remove} for deleting an element from a set.
|
|
3246
|
+
* @see {@link toggle} for toggling presence of an element.
|
|
3247
|
+
*/
|
|
3248
|
+
const add = (item) => (set) => {
|
|
3249
|
+
if (set.has(item)) return set;
|
|
3250
|
+
const result = new globalThis.Set(set);
|
|
2791
3251
|
result.add(item);
|
|
2792
3252
|
return result;
|
|
2793
3253
|
};
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
3254
|
+
/**
|
|
3255
|
+
* Returns a new set with the element removed.
|
|
3256
|
+
*
|
|
3257
|
+
* @see {@link add} for adding an element to a set.
|
|
3258
|
+
*/
|
|
3259
|
+
const remove = (item) => (set) => {
|
|
3260
|
+
if (!set.has(item)) return set;
|
|
3261
|
+
const result = new globalThis.Set(set);
|
|
2797
3262
|
result.delete(item);
|
|
2798
3263
|
return result;
|
|
2799
3264
|
};
|
|
2800
|
-
|
|
2801
|
-
|
|
3265
|
+
/**
|
|
3266
|
+
* Toggles presence of an element in a set.
|
|
3267
|
+
*
|
|
3268
|
+
* @see {@link add} for unconditionally adding an element.
|
|
3269
|
+
* @see {@link remove} for unconditionally removing an element.
|
|
3270
|
+
*/
|
|
3271
|
+
const toggle = (item) => (set) => {
|
|
3272
|
+
const result = new globalThis.Set(set);
|
|
2802
3273
|
if (result.has(item)) result.delete(item);
|
|
2803
3274
|
else result.add(item);
|
|
2804
3275
|
return result;
|
|
2805
3276
|
};
|
|
2806
|
-
|
|
3277
|
+
/**
|
|
3278
|
+
* Transforms each element of a set into a new set.
|
|
3279
|
+
*
|
|
3280
|
+
* @see {@link filterMap} for mapping and filtering in a single step.
|
|
3281
|
+
*/
|
|
3282
|
+
const map = (transform) => (set) => {
|
|
2807
3283
|
const result = new globalThis.Set();
|
|
2808
|
-
for (const item of
|
|
3284
|
+
for (const item of set) result.add(transform(item));
|
|
2809
3285
|
return result;
|
|
2810
3286
|
};
|
|
2811
|
-
|
|
3287
|
+
/**
|
|
3288
|
+
* Filters elements of a set by a predicate.
|
|
3289
|
+
*
|
|
3290
|
+
* @see {@link filterMap} for filtering and mapping simultaneously.
|
|
3291
|
+
*/
|
|
3292
|
+
const filter = (predicate) => (set) => {
|
|
2812
3293
|
const result = new globalThis.Set();
|
|
2813
|
-
for (const item of
|
|
3294
|
+
for (const item of set) if (predicate(item)) result.add(item);
|
|
2814
3295
|
return result;
|
|
2815
3296
|
};
|
|
2816
|
-
|
|
3297
|
+
/**
|
|
3298
|
+
* Transforms elements with a function returning `Maybe`, keeping only `Some` values.
|
|
3299
|
+
*
|
|
3300
|
+
* @see {@link filter} for filtering with a boolean predicate.
|
|
3301
|
+
* @see {@link map} for transforming without filtering.
|
|
3302
|
+
*/
|
|
3303
|
+
const filterMap = (transform) => (set) => {
|
|
2817
3304
|
const result = new globalThis.Set();
|
|
2818
|
-
for (const item of
|
|
2819
|
-
const mb =
|
|
3305
|
+
for (const item of set) {
|
|
3306
|
+
const mb = transform(item);
|
|
2820
3307
|
if (mb.kind === "Some") result.add(mb.value);
|
|
2821
3308
|
}
|
|
2822
3309
|
return result;
|
|
2823
3310
|
};
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
3311
|
+
/**
|
|
3312
|
+
* Computes the union of two sets.
|
|
3313
|
+
*
|
|
3314
|
+
* @see {@link intersection} for keeping only elements present in both sets.
|
|
3315
|
+
* @see {@link difference} for subtracting elements.
|
|
3316
|
+
*/
|
|
3317
|
+
const union = (other) => (set) => {
|
|
3318
|
+
const s = set;
|
|
3319
|
+
if (typeof s.union === "function") return s.union(other);
|
|
3320
|
+
const result = new globalThis.Set(set);
|
|
2828
3321
|
for (const item of other) result.add(item);
|
|
2829
3322
|
return result;
|
|
2830
3323
|
};
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
3324
|
+
/**
|
|
3325
|
+
* Computes the intersection of two sets.
|
|
3326
|
+
*
|
|
3327
|
+
* @see {@link union} for combining elements from both sets.
|
|
3328
|
+
* @see {@link difference} for subtracting elements.
|
|
3329
|
+
*/
|
|
3330
|
+
const intersection = (other) => (set) => {
|
|
3331
|
+
const s = set;
|
|
3332
|
+
if (typeof s.intersection === "function") return s.intersection(other);
|
|
2834
3333
|
const result = new globalThis.Set();
|
|
2835
|
-
for (const item of
|
|
3334
|
+
for (const item of set) if (other.has(item)) result.add(item);
|
|
2836
3335
|
return result;
|
|
2837
3336
|
};
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
3337
|
+
/**
|
|
3338
|
+
* Computes the set difference (`set \ other`).
|
|
3339
|
+
*
|
|
3340
|
+
* @see {@link union} for combining all elements.
|
|
3341
|
+
* @see {@link intersection} for keeping only common elements.
|
|
3342
|
+
*/
|
|
3343
|
+
const difference = (other) => (set) => {
|
|
3344
|
+
const s = set;
|
|
3345
|
+
if (typeof s.difference === "function") return s.difference(other);
|
|
2841
3346
|
const result = new globalThis.Set();
|
|
2842
|
-
for (const item of
|
|
3347
|
+
for (const item of set) if (!other.has(item)) result.add(item);
|
|
2843
3348
|
return result;
|
|
2844
3349
|
};
|
|
2845
|
-
const isSubsetOf = (other) => (
|
|
2846
|
-
const
|
|
2847
|
-
if (typeof
|
|
2848
|
-
for (const item of
|
|
3350
|
+
const isSubsetOf = (other) => (set) => {
|
|
3351
|
+
const s = set;
|
|
3352
|
+
if (typeof s.isSubsetOf === "function") return s.isSubsetOf(other);
|
|
3353
|
+
for (const item of set) if (!other.has(item)) return false;
|
|
2849
3354
|
return true;
|
|
2850
3355
|
};
|
|
2851
|
-
const reduce = (
|
|
2852
|
-
let acc =
|
|
2853
|
-
for (const item of
|
|
3356
|
+
const reduce = (initial, reducer) => (set) => {
|
|
3357
|
+
let acc = initial;
|
|
3358
|
+
for (const item of set) acc = reducer(acc, item);
|
|
2854
3359
|
return acc;
|
|
2855
3360
|
};
|
|
2856
|
-
const toArray = (
|
|
3361
|
+
const toArray = (set) => [...set];
|
|
2857
3362
|
const Uniq = {
|
|
2858
3363
|
is: {
|
|
2859
3364
|
empty: isEmpty,
|
|
2860
|
-
nonEmpty: isNonEmpty
|
|
3365
|
+
nonEmpty: isNonEmpty,
|
|
3366
|
+
subsetOf: isSubsetOf
|
|
2861
3367
|
},
|
|
2862
3368
|
empty,
|
|
2863
3369
|
singleton,
|
|
2864
|
-
from: {
|
|
3370
|
+
from: { array: fromArray },
|
|
2865
3371
|
has,
|
|
2866
3372
|
size,
|
|
2867
3373
|
add,
|
|
@@ -2874,15 +3380,14 @@ const Uniq = {
|
|
|
2874
3380
|
union,
|
|
2875
3381
|
intersection,
|
|
2876
3382
|
difference,
|
|
2877
|
-
isSubsetOf,
|
|
2878
3383
|
reduce,
|
|
2879
|
-
to: {
|
|
3384
|
+
to: { array: toArray },
|
|
2880
3385
|
NonEmpty: {
|
|
2881
3386
|
singleton: (item) => new globalThis.Set([item]),
|
|
2882
|
-
from: {
|
|
2883
|
-
reduce: (
|
|
2884
|
-
map: (
|
|
2885
|
-
to: {
|
|
3387
|
+
from: { set: (set) => set.size > 0 ? Maybe.make.some(set) : Maybe.make.none() },
|
|
3388
|
+
reduce: (reducer) => (set) => toArray(set).reduce(reducer),
|
|
3389
|
+
map: (transform) => (set) => map(transform)(set),
|
|
3390
|
+
to: { array: (set) => toArray(set) }
|
|
2886
3391
|
}
|
|
2887
3392
|
};
|
|
2888
3393
|
//#endregion
|