@nlozgachev/pipelined 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/esm/src/Core/Option.js +2 -1
- package/esm/src/Core/Optional.js +2 -2
- package/esm/src/Core/RemoteData.js +8 -8
- package/esm/src/Core/TaskValidation.js +35 -12
- package/esm/src/Core/Validation.js +3 -3
- package/esm/src/Core/index.js +0 -2
- package/esm/src/{Core → Utils}/Arr.js +95 -23
- package/esm/src/Utils/Num.js +124 -0
- package/esm/src/{Core → Utils}/Rec.js +59 -11
- package/esm/src/Utils/Str.js +134 -0
- package/esm/src/Utils/index.js +4 -0
- package/package.json +11 -1
- package/script/src/Core/Option.js +2 -1
- package/script/src/Core/Optional.js +2 -2
- package/script/src/Core/RemoteData.js +8 -8
- package/script/src/Core/TaskValidation.js +35 -12
- package/script/src/Core/Validation.js +3 -3
- package/script/src/Core/index.js +0 -2
- package/script/src/{Core → Utils}/Arr.js +95 -23
- package/script/src/Utils/Num.js +127 -0
- package/script/src/{Core → Utils}/Rec.js +59 -11
- package/script/src/Utils/Str.js +137 -0
- package/script/src/Utils/index.js +20 -0
- package/types/src/Composition/compose.d.ts.map +1 -1
- package/types/src/Composition/converge.d.ts.map +1 -1
- package/types/src/Composition/curry.d.ts.map +1 -1
- package/types/src/Composition/flow.d.ts.map +1 -1
- package/types/src/Composition/fn.d.ts.map +1 -1
- package/types/src/Composition/juxt.d.ts.map +1 -1
- package/types/src/Composition/memoize.d.ts.map +1 -1
- package/types/src/Composition/not.d.ts.map +1 -1
- package/types/src/Composition/on.d.ts.map +1 -1
- package/types/src/Composition/pipe.d.ts.map +1 -1
- package/types/src/Composition/uncurry.d.ts.map +1 -1
- package/types/src/Core/Deferred.d.ts.map +1 -1
- package/types/src/Core/Lens.d.ts.map +1 -1
- package/types/src/Core/Logged.d.ts.map +1 -1
- package/types/src/Core/Option.d.ts +1 -1
- package/types/src/Core/Option.d.ts.map +1 -1
- package/types/src/Core/Optional.d.ts +2 -2
- package/types/src/Core/Optional.d.ts.map +1 -1
- package/types/src/Core/Predicate.d.ts.map +1 -1
- package/types/src/Core/Reader.d.ts.map +1 -1
- package/types/src/Core/Refinement.d.ts.map +1 -1
- package/types/src/Core/RemoteData.d.ts +3 -3
- package/types/src/Core/RemoteData.d.ts.map +1 -1
- package/types/src/Core/Result.d.ts +1 -1
- package/types/src/Core/Result.d.ts.map +1 -1
- package/types/src/Core/State.d.ts.map +1 -1
- package/types/src/Core/Task.d.ts.map +1 -1
- package/types/src/Core/TaskOption.d.ts +1 -1
- package/types/src/Core/TaskOption.d.ts.map +1 -1
- package/types/src/Core/TaskResult.d.ts.map +1 -1
- package/types/src/Core/TaskValidation.d.ts +31 -8
- package/types/src/Core/TaskValidation.d.ts.map +1 -1
- package/types/src/Core/These.d.ts.map +1 -1
- package/types/src/Core/Validation.d.ts +3 -3
- package/types/src/Core/Validation.d.ts.map +1 -1
- package/types/src/Core/index.d.ts +0 -2
- package/types/src/Core/index.d.ts.map +1 -1
- package/types/src/Types/Brand.d.ts.map +1 -1
- package/types/src/Types/NonEmptyList.d.ts.map +1 -1
- package/types/src/{Core → Utils}/Arr.d.ts +25 -3
- package/types/src/Utils/Arr.d.ts.map +1 -0
- package/types/src/Utils/Num.d.ts +110 -0
- package/types/src/Utils/Num.d.ts.map +1 -0
- package/types/src/{Core → Utils}/Rec.d.ts +23 -1
- package/types/src/Utils/Rec.d.ts.map +1 -0
- package/types/src/Utils/Str.d.ts +128 -0
- package/types/src/Utils/Str.d.ts.map +1 -0
- package/types/src/Utils/index.d.ts +5 -0
- package/types/src/Utils/index.d.ts.map +1 -0
- package/types/src/Core/Arr.d.ts.map +0 -1
- package/types/src/Core/Rec.d.ts.map +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Option } from "../Core/Option.js";
|
|
2
|
+
/**
|
|
3
|
+
* String utilities. All transformation functions are data-last and curried so they
|
|
4
|
+
* compose naturally with `pipe`. Safe parsers return `Option` instead of `NaN`.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Str } from "@nlozgachev/pipelined/utils";
|
|
9
|
+
* import { pipe } from "@nlozgachev/pipelined/composition";
|
|
10
|
+
*
|
|
11
|
+
* pipe(" Hello, World! ", Str.trim, Str.toLowerCase); // "hello, world!"
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export var Str;
|
|
15
|
+
(function (Str) {
|
|
16
|
+
/**
|
|
17
|
+
* Splits a string by a separator. Data-last: use in `pipe`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* pipe("a,b,c", Str.split(",")); // ["a", "b", "c"]
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
Str.split = (separator) => (s) => s.split(separator);
|
|
25
|
+
/**
|
|
26
|
+
* Removes leading and trailing whitespace from a string.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* pipe(" hello ", Str.trim); // "hello"
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
Str.trim = (s) => s.trim();
|
|
34
|
+
/**
|
|
35
|
+
* Returns `true` when the string contains the given substring.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* pipe("hello world", Str.includes("world")); // true
|
|
40
|
+
* pipe("hello world", Str.includes("xyz")); // false
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
Str.includes = (substring) => (s) => s.includes(substring);
|
|
44
|
+
/**
|
|
45
|
+
* Returns `true` when the string starts with the given prefix.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* pipe("hello world", Str.startsWith("hello")); // true
|
|
50
|
+
* pipe("hello world", Str.startsWith("world")); // false
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
Str.startsWith = (prefix) => (s) => s.startsWith(prefix);
|
|
54
|
+
/**
|
|
55
|
+
* Returns `true` when the string ends with the given suffix.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* pipe("hello world", Str.endsWith("world")); // true
|
|
60
|
+
* pipe("hello world", Str.endsWith("hello")); // false
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
Str.endsWith = (suffix) => (s) => s.endsWith(suffix);
|
|
64
|
+
/**
|
|
65
|
+
* Converts a string to uppercase.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* pipe("hello", Str.toUpperCase); // "HELLO"
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
Str.toUpperCase = (s) => s.toUpperCase();
|
|
73
|
+
/**
|
|
74
|
+
* Converts a string to lowercase.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* pipe("HELLO", Str.toLowerCase); // "hello"
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
Str.toLowerCase = (s) => s.toLowerCase();
|
|
82
|
+
/**
|
|
83
|
+
* Splits a string into lines, normalising `\r\n` and `\r` line endings.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* Str.lines("one\ntwo\nthree"); // ["one", "two", "three"]
|
|
88
|
+
* Str.lines("a\r\nb"); // ["a", "b"]
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
Str.lines = (s) => s.split(/\r?\n|\r/);
|
|
92
|
+
/**
|
|
93
|
+
* Splits a string into words on any whitespace boundary, filtering out empty strings.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* Str.words(" hello world "); // ["hello", "world"]
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
Str.words = (s) => s.trim().split(/\s+/).filter(Boolean);
|
|
101
|
+
/**
|
|
102
|
+
* Safe number parsers that return `Option` instead of `NaN`.
|
|
103
|
+
*/
|
|
104
|
+
Str.parse = {
|
|
105
|
+
/**
|
|
106
|
+
* Parses a string as an integer (base 10). Returns `None` if the result is `NaN`.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* Str.parse.int("42"); // Some(42)
|
|
111
|
+
* Str.parse.int("3.7"); // Some(3)
|
|
112
|
+
* Str.parse.int("abc"); // None
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
int: (s) => {
|
|
116
|
+
const n = parseInt(s, 10);
|
|
117
|
+
return isNaN(n) ? Option.none() : Option.some(n);
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Parses a string as a floating-point number. Returns `None` if the result is `NaN`.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* Str.parse.float("3.14"); // Some(3.14)
|
|
125
|
+
* Str.parse.float("42"); // Some(42)
|
|
126
|
+
* Str.parse.float("abc"); // None
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
float: (s) => {
|
|
130
|
+
const n = parseFloat(s);
|
|
131
|
+
return isNaN(n) ? Option.none() : Option.some(n);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
})(Str || (Str = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nlozgachev/pipelined",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Simple functional programming toolkit for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"functional",
|
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
"default": "./script/src/Core/index.js"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
+
"./utils": {
|
|
41
|
+
"import": {
|
|
42
|
+
"types": "./types/src/Utils/index.d.ts",
|
|
43
|
+
"default": "./esm/src/Utils/index.js"
|
|
44
|
+
},
|
|
45
|
+
"require": {
|
|
46
|
+
"types": "./types/src/Utils/index.d.ts",
|
|
47
|
+
"default": "./script/src/Utils/index.js"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
40
50
|
"./types": {
|
|
41
51
|
"import": {
|
|
42
52
|
"types": "./types/src/Types/index.d.ts",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Option = void 0;
|
|
4
4
|
const Result_js_1 = require("./Result.js");
|
|
5
|
+
const _none = { kind: "None" };
|
|
5
6
|
var Option;
|
|
6
7
|
(function (Option) {
|
|
7
8
|
/**
|
|
@@ -15,7 +16,7 @@ var Option;
|
|
|
15
16
|
/**
|
|
16
17
|
* Creates a None (empty Option).
|
|
17
18
|
*/
|
|
18
|
-
Option.none = () =>
|
|
19
|
+
Option.none = () => _none;
|
|
19
20
|
/**
|
|
20
21
|
* Type guard that checks if a Option is None.
|
|
21
22
|
*/
|
|
@@ -87,12 +87,12 @@ var Optional;
|
|
|
87
87
|
*
|
|
88
88
|
* @example
|
|
89
89
|
* ```ts
|
|
90
|
-
* pipe(profile, Optional.getOrElse(bioOpt)("no bio"));
|
|
90
|
+
* pipe(profile, Optional.getOrElse(bioOpt)(() => "no bio"));
|
|
91
91
|
* ```
|
|
92
92
|
*/
|
|
93
93
|
Optional.getOrElse = (opt) => (defaultValue) => (s) => {
|
|
94
94
|
const val = opt.get(s);
|
|
95
|
-
return val.kind === "Some" ? val.value : defaultValue;
|
|
95
|
+
return val.kind === "Some" ? val.value : defaultValue();
|
|
96
96
|
};
|
|
97
97
|
/**
|
|
98
98
|
* Extracts a value from an Optional focus using handlers for the present
|
|
@@ -3,16 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RemoteData = void 0;
|
|
4
4
|
const Option_js_1 = require("./Option.js");
|
|
5
5
|
const Result_js_1 = require("./Result.js");
|
|
6
|
+
const _notAsked = { kind: "NotAsked" };
|
|
7
|
+
const _loading = { kind: "Loading" };
|
|
6
8
|
var RemoteData;
|
|
7
9
|
(function (RemoteData) {
|
|
8
10
|
/**
|
|
9
11
|
* Creates a NotAsked RemoteData.
|
|
10
12
|
*/
|
|
11
|
-
RemoteData.notAsked = () =>
|
|
13
|
+
RemoteData.notAsked = () => _notAsked;
|
|
12
14
|
/**
|
|
13
15
|
* Creates a Loading RemoteData.
|
|
14
16
|
*/
|
|
15
|
-
RemoteData.loading = () =>
|
|
17
|
+
RemoteData.loading = () => _loading;
|
|
16
18
|
/**
|
|
17
19
|
* Creates a Failure RemoteData with the given error.
|
|
18
20
|
*/
|
|
@@ -162,9 +164,9 @@ var RemoteData;
|
|
|
162
164
|
*
|
|
163
165
|
* @example
|
|
164
166
|
* ```ts
|
|
165
|
-
* pipe(RemoteData.success(5), RemoteData.getOrElse(0)); // 5
|
|
166
|
-
* pipe(RemoteData.loading(), RemoteData.getOrElse(0)); // 0
|
|
167
|
-
* pipe(RemoteData.loading<string, number>(), RemoteData.getOrElse(null)); // null — typed as number | null
|
|
167
|
+
* pipe(RemoteData.success(5), RemoteData.getOrElse(() => 0)); // 5
|
|
168
|
+
* pipe(RemoteData.loading(), RemoteData.getOrElse(() => 0)); // 0
|
|
169
|
+
* pipe(RemoteData.loading<string, number>(), RemoteData.getOrElse(() => null)); // null — typed as number | null
|
|
168
170
|
* ```
|
|
169
171
|
*/
|
|
170
172
|
RemoteData.getOrElse = (defaultValue) => (data) => RemoteData.isSuccess(data) ? data.value : defaultValue();
|
|
@@ -208,7 +210,5 @@ var RemoteData;
|
|
|
208
210
|
* ); // Ok(42)
|
|
209
211
|
* ```
|
|
210
212
|
*/
|
|
211
|
-
RemoteData.toResult = (onNotReady) => (data) => RemoteData.isSuccess(data)
|
|
212
|
-
? Result_js_1.Result.ok(data.value)
|
|
213
|
-
: Result_js_1.Result.err(RemoteData.isFailure(data) ? data.error : onNotReady());
|
|
213
|
+
RemoteData.toResult = (onNotReady) => (data) => RemoteData.isSuccess(data) ? Result_js_1.Result.ok(data.value) : Result_js_1.Result.err(RemoteData.isFailure(data) ? data.error : onNotReady());
|
|
214
214
|
})(RemoteData || (exports.RemoteData = RemoteData = {}));
|
|
@@ -42,15 +42,6 @@ var TaskValidation;
|
|
|
42
42
|
* Transforms the success value inside a TaskValidation.
|
|
43
43
|
*/
|
|
44
44
|
TaskValidation.map = (f) => (data) => Task_js_1.Task.map(Validation_js_1.Validation.map(f))(data);
|
|
45
|
-
/**
|
|
46
|
-
* Chains TaskValidation computations. If the first is Valid, passes the value
|
|
47
|
-
* to f. If the first is Invalid, propagates the errors.
|
|
48
|
-
*
|
|
49
|
-
* Note: chain short-circuits on first error. Use ap to accumulate errors.
|
|
50
|
-
*/
|
|
51
|
-
TaskValidation.chain = (f) => (data) => Task_js_1.Task.chain((validation) => Validation_js_1.Validation.isValid(validation)
|
|
52
|
-
? f(validation.value)
|
|
53
|
-
: Task_js_1.Task.resolve(Validation_js_1.Validation.invalidAll(validation.errors)))(data);
|
|
54
45
|
/**
|
|
55
46
|
* Applies a function wrapped in a TaskValidation to a value wrapped in a
|
|
56
47
|
* TaskValidation. Both Tasks run in parallel and errors from both sides
|
|
@@ -100,9 +91,41 @@ var TaskValidation;
|
|
|
100
91
|
TaskValidation.tap = (f) => (data) => Task_js_1.Task.map(Validation_js_1.Validation.tap(f))(data);
|
|
101
92
|
/**
|
|
102
93
|
* Recovers from an Invalid state by providing a fallback TaskValidation.
|
|
94
|
+
* The fallback receives the accumulated error list so callers can inspect which errors occurred.
|
|
103
95
|
* The fallback can produce a different success type, widening the result to `TaskValidation<E, A | B>`.
|
|
104
96
|
*/
|
|
105
|
-
TaskValidation.recover = (fallback) => (data) => Task_js_1.Task.chain((validation) => Validation_js_1.Validation.isValid(validation)
|
|
106
|
-
|
|
107
|
-
|
|
97
|
+
TaskValidation.recover = (fallback) => (data) => Task_js_1.Task.chain((validation) => Validation_js_1.Validation.isValid(validation) ? Task_js_1.Task.resolve(validation) : fallback(validation.errors))(data);
|
|
98
|
+
/**
|
|
99
|
+
* Runs two TaskValidations concurrently and combines their results into a tuple.
|
|
100
|
+
* If both are Valid, returns Valid with both values. If either fails, accumulates
|
|
101
|
+
* errors from both sides.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* await TaskValidation.product(
|
|
106
|
+
* validateName(form.name),
|
|
107
|
+
* validateAge(form.age),
|
|
108
|
+
* )(); // Valid(["Alice", 30]) or Invalid([...errors])
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
TaskValidation.product = (first, second) => Task_js_1.Task.from(() => Promise.all([
|
|
112
|
+
Deferred_js_1.Deferred.toPromise(first()),
|
|
113
|
+
Deferred_js_1.Deferred.toPromise(second()),
|
|
114
|
+
]).then(([va, vb]) => Validation_js_1.Validation.product(va, vb)));
|
|
115
|
+
/**
|
|
116
|
+
* Runs all TaskValidations concurrently and collects results.
|
|
117
|
+
* If all are Valid, returns Valid with all values as an array.
|
|
118
|
+
* If any fail, returns Invalid with all accumulated errors.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* await TaskValidation.productAll([
|
|
123
|
+
* validateName(form.name),
|
|
124
|
+
* validateEmail(form.email),
|
|
125
|
+
* validateAge(form.age),
|
|
126
|
+
* ])(); // Valid([name, email, age]) or Invalid([...all errors])
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
TaskValidation.productAll = (data) => Task_js_1.Task.from(() => Promise.all(data.map((t) => Deferred_js_1.Deferred.toPromise(t())))
|
|
130
|
+
.then((results) => Validation_js_1.Validation.productAll(results)));
|
|
108
131
|
})(TaskValidation || (exports.TaskValidation = TaskValidation = {}));
|
|
@@ -122,9 +122,9 @@ var Validation;
|
|
|
122
122
|
*
|
|
123
123
|
* @example
|
|
124
124
|
* ```ts
|
|
125
|
-
* pipe(Validation.valid(5), Validation.getOrElse(0)); // 5
|
|
126
|
-
* pipe(Validation.invalid("oops"), Validation.getOrElse(0)); // 0
|
|
127
|
-
* pipe(Validation.invalid("oops"), Validation.getOrElse(null)); // null — typed as number | null
|
|
125
|
+
* pipe(Validation.valid(5), Validation.getOrElse(() => 0)); // 5
|
|
126
|
+
* pipe(Validation.invalid("oops"), Validation.getOrElse(() => 0)); // 0
|
|
127
|
+
* pipe(Validation.invalid("oops"), Validation.getOrElse(() => null)); // null — typed as number | null
|
|
128
128
|
* ```
|
|
129
129
|
*/
|
|
130
130
|
Validation.getOrElse = (defaultValue) => (data) => Validation.isValid(data) ? data.value : defaultValue();
|
package/script/src/Core/index.js
CHANGED
|
@@ -14,14 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./Arr.js"), exports);
|
|
18
17
|
__exportStar(require("./Logged.js"), exports);
|
|
19
18
|
__exportStar(require("./Deferred.js"), exports);
|
|
20
19
|
__exportStar(require("./Lens.js"), exports);
|
|
21
20
|
__exportStar(require("./Option.js"), exports);
|
|
22
21
|
__exportStar(require("./Reader.js"), exports);
|
|
23
22
|
__exportStar(require("./Optional.js"), exports);
|
|
24
|
-
__exportStar(require("./Rec.js"), exports);
|
|
25
23
|
__exportStar(require("./Predicate.js"), exports);
|
|
26
24
|
__exportStar(require("./Refinement.js"), exports);
|
|
27
25
|
__exportStar(require("./RemoteData.js"), exports);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Arr = void 0;
|
|
4
|
-
const Deferred_js_1 = require("
|
|
5
|
-
const Option_js_1 = require("
|
|
6
|
-
const Result_js_1 = require("
|
|
7
|
-
const Task_js_1 = require("
|
|
4
|
+
const Deferred_js_1 = require("../Core/Deferred.js");
|
|
5
|
+
const Option_js_1 = require("../Core/Option.js");
|
|
6
|
+
const Result_js_1 = require("../Core/Result.js");
|
|
7
|
+
const Task_js_1 = require("../Core/Task.js");
|
|
8
8
|
const NonEmptyList_js_1 = require("../Types/NonEmptyList.js");
|
|
9
9
|
/**
|
|
10
10
|
* Functional array utilities that compose well with pipe.
|
|
@@ -113,7 +113,13 @@ var Arr;
|
|
|
113
113
|
* pipe([1, 2, 3], Arr.map(n => n * 2)); // [2, 4, 6]
|
|
114
114
|
* ```
|
|
115
115
|
*/
|
|
116
|
-
Arr.map = (f) => (data) =>
|
|
116
|
+
Arr.map = (f) => (data) => {
|
|
117
|
+
const n = data.length;
|
|
118
|
+
const result = new Array(n);
|
|
119
|
+
for (let i = 0; i < n; i++)
|
|
120
|
+
result[i] = f(data[i]);
|
|
121
|
+
return result;
|
|
122
|
+
};
|
|
117
123
|
/**
|
|
118
124
|
* Filters elements that satisfy the predicate.
|
|
119
125
|
*
|
|
@@ -122,7 +128,15 @@ var Arr;
|
|
|
122
128
|
* pipe([1, 2, 3, 4], Arr.filter(n => n % 2 === 0)); // [2, 4]
|
|
123
129
|
* ```
|
|
124
130
|
*/
|
|
125
|
-
Arr.filter = (predicate) => (data) =>
|
|
131
|
+
Arr.filter = (predicate) => (data) => {
|
|
132
|
+
const n = data.length;
|
|
133
|
+
const result = [];
|
|
134
|
+
for (let i = 0; i < n; i++) {
|
|
135
|
+
if (predicate(data[i]))
|
|
136
|
+
result.push(data[i]);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
};
|
|
126
140
|
/**
|
|
127
141
|
* Splits an array into two groups based on a predicate.
|
|
128
142
|
* First group contains elements that satisfy the predicate,
|
|
@@ -216,9 +230,9 @@ var Arr;
|
|
|
216
230
|
*/
|
|
217
231
|
Arr.zip = (other) => (data) => {
|
|
218
232
|
const len = Math.min(data.length, other.length);
|
|
219
|
-
const result =
|
|
233
|
+
const result = new Array(len);
|
|
220
234
|
for (let i = 0; i < len; i++) {
|
|
221
|
-
result
|
|
235
|
+
result[i] = [data[i], other[i]];
|
|
222
236
|
}
|
|
223
237
|
return result;
|
|
224
238
|
};
|
|
@@ -232,9 +246,9 @@ var Arr;
|
|
|
232
246
|
*/
|
|
233
247
|
Arr.zipWith = (f) => (other) => (data) => {
|
|
234
248
|
const len = Math.min(data.length, other.length);
|
|
235
|
-
const result =
|
|
249
|
+
const result = new Array(len);
|
|
236
250
|
for (let i = 0; i < len; i++) {
|
|
237
|
-
result
|
|
251
|
+
result[i] = f(data[i], other[i]);
|
|
238
252
|
}
|
|
239
253
|
return result;
|
|
240
254
|
};
|
|
@@ -289,7 +303,17 @@ var Arr;
|
|
|
289
303
|
* pipe([1, 2, 3], Arr.flatMap(n => [n, n * 10])); // [1, 10, 2, 20, 3, 30]
|
|
290
304
|
* ```
|
|
291
305
|
*/
|
|
292
|
-
Arr.flatMap = (f) => (data) =>
|
|
306
|
+
Arr.flatMap = (f) => (data) => {
|
|
307
|
+
const n = data.length;
|
|
308
|
+
const result = [];
|
|
309
|
+
for (let i = 0; i < n; i++) {
|
|
310
|
+
const chunk = f(data[i]);
|
|
311
|
+
const m = chunk.length;
|
|
312
|
+
for (let j = 0; j < m; j++)
|
|
313
|
+
result.push(chunk[j]);
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
316
|
+
};
|
|
293
317
|
/**
|
|
294
318
|
* Reduces an array from the left.
|
|
295
319
|
*
|
|
@@ -316,12 +340,13 @@ var Arr;
|
|
|
316
340
|
* ```
|
|
317
341
|
*/
|
|
318
342
|
Arr.traverse = (f) => (data) => {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
343
|
+
const n = data.length;
|
|
344
|
+
const result = new Array(n);
|
|
345
|
+
for (let i = 0; i < n; i++) {
|
|
346
|
+
const mapped = f(data[i]);
|
|
347
|
+
if (mapped.kind === "None")
|
|
323
348
|
return Option_js_1.Option.none();
|
|
324
|
-
result
|
|
349
|
+
result[i] = mapped.value;
|
|
325
350
|
}
|
|
326
351
|
return Option_js_1.Option.some(result);
|
|
327
352
|
};
|
|
@@ -338,12 +363,13 @@ var Arr;
|
|
|
338
363
|
* ```
|
|
339
364
|
*/
|
|
340
365
|
Arr.traverseResult = (f) => (data) => {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
366
|
+
const n = data.length;
|
|
367
|
+
const result = new Array(n);
|
|
368
|
+
for (let i = 0; i < n; i++) {
|
|
369
|
+
const mapped = f(data[i]);
|
|
370
|
+
if (mapped.kind === "Error")
|
|
345
371
|
return mapped;
|
|
346
|
-
result
|
|
372
|
+
result[i] = mapped.value;
|
|
347
373
|
}
|
|
348
374
|
return Result_js_1.Result.ok(result);
|
|
349
375
|
};
|
|
@@ -430,7 +456,13 @@ var Arr;
|
|
|
430
456
|
* pipe([1, 2, 3], Arr.some(n => n > 2)); // true
|
|
431
457
|
* ```
|
|
432
458
|
*/
|
|
433
|
-
Arr.some = (predicate) => (data) =>
|
|
459
|
+
Arr.some = (predicate) => (data) => {
|
|
460
|
+
const n = data.length;
|
|
461
|
+
for (let i = 0; i < n; i++)
|
|
462
|
+
if (predicate(data[i]))
|
|
463
|
+
return true;
|
|
464
|
+
return false;
|
|
465
|
+
};
|
|
434
466
|
/**
|
|
435
467
|
* Returns true if all elements satisfy the predicate.
|
|
436
468
|
*
|
|
@@ -439,7 +471,13 @@ var Arr;
|
|
|
439
471
|
* pipe([1, 2, 3], Arr.every(n => n > 0)); // true
|
|
440
472
|
* ```
|
|
441
473
|
*/
|
|
442
|
-
Arr.every = (predicate) => (data) =>
|
|
474
|
+
Arr.every = (predicate) => (data) => {
|
|
475
|
+
const n = data.length;
|
|
476
|
+
for (let i = 0; i < n; i++)
|
|
477
|
+
if (!predicate(data[i]))
|
|
478
|
+
return false;
|
|
479
|
+
return true;
|
|
480
|
+
};
|
|
443
481
|
/**
|
|
444
482
|
* Reverses an array. Returns a new array.
|
|
445
483
|
*
|
|
@@ -498,4 +536,38 @@ var Arr;
|
|
|
498
536
|
i++;
|
|
499
537
|
return data.slice(i);
|
|
500
538
|
};
|
|
539
|
+
/**
|
|
540
|
+
* Like `reduce`, but returns every intermediate accumulator as an array.
|
|
541
|
+
* The initial value is not included — the output has the same length as the input.
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```ts
|
|
545
|
+
* pipe([1, 2, 3], Arr.scan(0, (acc, n) => acc + n)); // [1, 3, 6]
|
|
546
|
+
* ```
|
|
547
|
+
*/
|
|
548
|
+
Arr.scan = (initial, f) => (data) => {
|
|
549
|
+
const n = data.length;
|
|
550
|
+
const result = new Array(n);
|
|
551
|
+
let acc = initial;
|
|
552
|
+
for (let i = 0; i < n; i++) {
|
|
553
|
+
acc = f(acc, data[i]);
|
|
554
|
+
result[i] = acc;
|
|
555
|
+
}
|
|
556
|
+
return result;
|
|
557
|
+
};
|
|
558
|
+
/**
|
|
559
|
+
* Splits an array at an index into a `[before, after]` tuple.
|
|
560
|
+
* Negative indices clamp to 0; indices beyond the array length clamp to the end.
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* pipe([1, 2, 3, 4], Arr.splitAt(2)); // [[1, 2], [3, 4]]
|
|
565
|
+
* pipe([1, 2, 3], Arr.splitAt(0)); // [[], [1, 2, 3]]
|
|
566
|
+
* pipe([1, 2, 3], Arr.splitAt(10)); // [[1, 2, 3], []]
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
Arr.splitAt = (index) => (data) => {
|
|
570
|
+
const i = Math.max(0, index);
|
|
571
|
+
return [data.slice(0, i), data.slice(i)];
|
|
572
|
+
};
|
|
501
573
|
})(Arr || (exports.Arr = Arr = {}));
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Num = void 0;
|
|
4
|
+
const Option_js_1 = require("../Core/Option.js");
|
|
5
|
+
/**
|
|
6
|
+
* Number utilities for common operations. All transformation functions are data-last
|
|
7
|
+
* and curried so they compose naturally with `pipe` and `Arr.map`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { Num } from "@nlozgachev/pipelined/utils";
|
|
12
|
+
* import { pipe } from "@nlozgachev/pipelined/composition";
|
|
13
|
+
*
|
|
14
|
+
* pipe(
|
|
15
|
+
* Num.range(1, 6),
|
|
16
|
+
* Arr.map(Num.multiply(2)),
|
|
17
|
+
* Arr.filter(Num.between(4, 8))
|
|
18
|
+
* ); // [4, 6, 8]
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
var Num;
|
|
22
|
+
(function (Num) {
|
|
23
|
+
/**
|
|
24
|
+
* Generates an array of numbers from `from` to `to` (both inclusive),
|
|
25
|
+
* stepping by `step` (default `1`). If `step` is negative or zero, or `from > to`,
|
|
26
|
+
* returns an empty array. When `step` does not land exactly on `to`, the last value
|
|
27
|
+
* is the largest reachable value that does not exceed `to`.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* Num.range(0, 5); // [0, 1, 2, 3, 4, 5]
|
|
32
|
+
* Num.range(0, 10, 2); // [0, 2, 4, 6, 8, 10]
|
|
33
|
+
* Num.range(0, 9, 2); // [0, 2, 4, 6, 8]
|
|
34
|
+
* Num.range(5, 0); // []
|
|
35
|
+
* Num.range(3, 3); // [3]
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
Num.range = (from, to, step = 1) => {
|
|
39
|
+
if (step <= 0 || from > to)
|
|
40
|
+
return [];
|
|
41
|
+
const count = Math.floor((to - from) / step) + 1;
|
|
42
|
+
const result = new Array(count);
|
|
43
|
+
for (let i = 0; i < count; i++) {
|
|
44
|
+
result[i] = from + i * step;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Clamps a number between `min` and `max` (both inclusive).
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* pipe(150, Num.clamp(0, 100)); // 100
|
|
54
|
+
* pipe(-5, Num.clamp(0, 100)); // 0
|
|
55
|
+
* pipe(42, Num.clamp(0, 100)); // 42
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
Num.clamp = (min, max) => (n) => Math.min(Math.max(n, min), max);
|
|
59
|
+
/**
|
|
60
|
+
* Returns `true` when the number is between `min` and `max` (both inclusive).
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* pipe(5, Num.between(1, 10)); // true
|
|
65
|
+
* pipe(0, Num.between(1, 10)); // false
|
|
66
|
+
* pipe(10, Num.between(1, 10)); // true
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
Num.between = (min, max) => (n) => n >= min && n <= max;
|
|
70
|
+
/**
|
|
71
|
+
* Parses a string as a number. Returns `None` when the result is `NaN`.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* Num.parse("42"); // Some(42)
|
|
76
|
+
* Num.parse("3.14"); // Some(3.14)
|
|
77
|
+
* Num.parse("abc"); // None
|
|
78
|
+
* Num.parse(""); // None
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
Num.parse = (s) => {
|
|
82
|
+
if (s.trim() === "")
|
|
83
|
+
return Option_js_1.Option.none();
|
|
84
|
+
const n = Number(s);
|
|
85
|
+
return isNaN(n) ? Option_js_1.Option.none() : Option_js_1.Option.some(n);
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Adds `b` to a number. Data-last: use in `pipe` or `Arr.map`.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* pipe(5, Num.add(3)); // 8
|
|
93
|
+
* pipe([1, 2, 3], Arr.map(Num.add(10))); // [11, 12, 13]
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
Num.add = (b) => (a) => a + b;
|
|
97
|
+
/**
|
|
98
|
+
* Subtracts `b` from a number. Data-last: `subtract(b)(a)` = `a - b`.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* pipe(10, Num.subtract(3)); // 7
|
|
103
|
+
* pipe([5, 10, 15], Arr.map(Num.subtract(2))); // [3, 8, 13]
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
Num.subtract = (b) => (a) => a - b;
|
|
107
|
+
/**
|
|
108
|
+
* Multiplies a number by `b`. Data-last: use in `pipe` or `Arr.map`.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* pipe(6, Num.multiply(7)); // 42
|
|
113
|
+
* pipe([1, 2, 3], Arr.map(Num.multiply(100))); // [100, 200, 300]
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
Num.multiply = (b) => (a) => a * b;
|
|
117
|
+
/**
|
|
118
|
+
* Divides a number by `b`. Data-last: `divide(b)(a)` = `a / b`.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* pipe(20, Num.divide(4)); // 5
|
|
123
|
+
* pipe([10, 20, 30], Arr.map(Num.divide(10))); // [1, 2, 3]
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
Num.divide = (b) => (a) => a / b;
|
|
127
|
+
})(Num || (exports.Num = Num = {}));
|