1 | ;
|
2 | /**
|
3 | * Higher order functions
|
4 | *
|
5 | * These utility functions are exported, but are subject to change without notice.
|
6 | *
|
7 | * @packageDocumentation
|
8 | */
|
9 | var __spreadArrays = (this && this.__spreadArrays) || function () {
|
10 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
11 | for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
12 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
13 | r[k] = a[j];
|
14 | return r;
|
15 | };
|
16 | Object.defineProperty(exports, "__esModule", { value: true });
|
17 | exports.pattern = exports.invoke = exports.val = exports.eq = exports.is = exports.any = exports.all = exports.or = exports.and = exports.not = exports.parse = exports.propEq = exports.prop = exports.pipe = exports.compose = exports.curry = void 0;
|
18 | /**
|
19 | * Returns a new function for [Partial Application](https://en.wikipedia.org/wiki/Partial_application) of the original function.
|
20 | *
|
21 | * Given a function with N parameters, returns a new function that supports partial application.
|
22 | * The new function accepts anywhere from 1 to N parameters. When that function is called with M parameters,
|
23 | * where M is less than N, it returns a new function that accepts the remaining parameters. It continues to
|
24 | * accept more parameters until all N parameters have been supplied.
|
25 | *
|
26 | *
|
27 | * This contrived example uses a partially applied function as an predicate, which returns true
|
28 | * if an object is found in both arrays.
|
29 | * @example
|
30 | * ```
|
31 | * // returns true if an object is in both of the two arrays
|
32 | * function inBoth(array1, array2, object) {
|
33 | * return array1.indexOf(object) !== -1 &&
|
34 | * array2.indexOf(object) !== 1;
|
35 | * }
|
36 | * let obj1, obj2, obj3, obj4, obj5, obj6, obj7
|
37 | * let foos = [obj1, obj3]
|
38 | * let bars = [obj3, obj4, obj5]
|
39 | *
|
40 | * // A curried "copy" of inBoth
|
41 | * let curriedInBoth = curry(inBoth);
|
42 | * // Partially apply both the array1 and array2
|
43 | * let inFoosAndBars = curriedInBoth(foos, bars);
|
44 | *
|
45 | * // Supply the final argument; since all arguments are
|
46 | * // supplied, the original inBoth function is then called.
|
47 | * let obj1InBoth = inFoosAndBars(obj1); // false
|
48 | *
|
49 | * // Use the inFoosAndBars as a predicate.
|
50 | * // Filter, on each iteration, supplies the final argument
|
51 | * let allObjs = [ obj1, obj2, obj3, obj4, obj5, obj6, obj7 ];
|
52 | * let foundInBoth = allObjs.filter(inFoosAndBars); // [ obj3 ]
|
53 | *
|
54 | * ```
|
55 | *
|
56 | * @param fn
|
57 | * @returns {*|function(): (*|any)}
|
58 | */
|
59 | function curry(fn) {
|
60 | return function curried() {
|
61 | if (arguments.length >= fn.length) {
|
62 | return fn.apply(this, arguments);
|
63 | }
|
64 | var args = Array.prototype.slice.call(arguments);
|
65 | return curried.bind.apply(curried, __spreadArrays([this], args));
|
66 | };
|
67 | }
|
68 | exports.curry = curry;
|
69 | /**
|
70 | * Given a varargs list of functions, returns a function that composes the argument functions, right-to-left
|
71 | * given: f(x), g(x), h(x)
|
72 | * let composed = compose(f,g,h)
|
73 | * then, composed is: f(g(h(x)))
|
74 | */
|
75 | function compose() {
|
76 | var args = arguments;
|
77 | var start = args.length - 1;
|
78 | return function () {
|
79 | var i = start, result = args[start].apply(this, arguments);
|
80 | while (i--)
|
81 | result = args[i].call(this, result);
|
82 | return result;
|
83 | };
|
84 | }
|
85 | exports.compose = compose;
|
86 | /**
|
87 | * Given a varargs list of functions, returns a function that is composes the argument functions, left-to-right
|
88 | * given: f(x), g(x), h(x)
|
89 | * let piped = pipe(f,g,h);
|
90 | * then, piped is: h(g(f(x)))
|
91 | */
|
92 | function pipe() {
|
93 | var funcs = [];
|
94 | for (var _i = 0; _i < arguments.length; _i++) {
|
95 | funcs[_i] = arguments[_i];
|
96 | }
|
97 | return compose.apply(null, [].slice.call(arguments).reverse());
|
98 | }
|
99 | exports.pipe = pipe;
|
100 | /**
|
101 | * Given a property name, returns a function that returns that property from an object
|
102 | * let obj = { foo: 1, name: "blarg" };
|
103 | * let getName = prop("name");
|
104 | * getName(obj) === "blarg"
|
105 | */
|
106 | exports.prop = function (name) { return function (obj) { return obj && obj[name]; }; };
|
107 | /**
|
108 | * Given a property name and a value, returns a function that returns a boolean based on whether
|
109 | * the passed object has a property that matches the value
|
110 | * let obj = { foo: 1, name: "blarg" };
|
111 | * let getName = propEq("name", "blarg");
|
112 | * getName(obj) === true
|
113 | */
|
114 | exports.propEq = curry(function (name, _val, obj) { return obj && obj[name] === _val; });
|
115 | /**
|
116 | * Given a dotted property name, returns a function that returns a nested property from an object, or undefined
|
117 | * let obj = { id: 1, nestedObj: { foo: 1, name: "blarg" }, };
|
118 | * let getName = prop("nestedObj.name");
|
119 | * getName(obj) === "blarg"
|
120 | * let propNotFound = prop("this.property.doesnt.exist");
|
121 | * propNotFound(obj) === undefined
|
122 | */
|
123 | exports.parse = function (name) { return pipe.apply(null, name.split('.').map(exports.prop)); };
|
124 | /**
|
125 | * Given a function that returns a truthy or falsey value, returns a
|
126 | * function that returns the opposite (falsey or truthy) value given the same inputs
|
127 | */
|
128 | exports.not = function (fn) { return function () {
|
129 | var args = [];
|
130 | for (var _i = 0; _i < arguments.length; _i++) {
|
131 | args[_i] = arguments[_i];
|
132 | }
|
133 | return !fn.apply(null, args);
|
134 | }; };
|
135 | /**
|
136 | * Given two functions that return truthy or falsey values, returns a function that returns truthy
|
137 | * if both functions return truthy for the given arguments
|
138 | */
|
139 | function and(fn1, fn2) {
|
140 | return function () {
|
141 | var args = [];
|
142 | for (var _i = 0; _i < arguments.length; _i++) {
|
143 | args[_i] = arguments[_i];
|
144 | }
|
145 | return fn1.apply(null, args) && fn2.apply(null, args);
|
146 | };
|
147 | }
|
148 | exports.and = and;
|
149 | /**
|
150 | * Given two functions that return truthy or falsey values, returns a function that returns truthy
|
151 | * if at least one of the functions returns truthy for the given arguments
|
152 | */
|
153 | function or(fn1, fn2) {
|
154 | return function () {
|
155 | var args = [];
|
156 | for (var _i = 0; _i < arguments.length; _i++) {
|
157 | args[_i] = arguments[_i];
|
158 | }
|
159 | return fn1.apply(null, args) || fn2.apply(null, args);
|
160 | };
|
161 | }
|
162 | exports.or = or;
|
163 | /**
|
164 | * Check if all the elements of an array match a predicate function
|
165 | *
|
166 | * @param fn1 a predicate function `fn1`
|
167 | * @returns a function which takes an array and returns true if `fn1` is true for all elements of the array
|
168 | */
|
169 | exports.all = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b && !!fn1(x); }, true); }; };
|
170 | // tslint:disable-next-line:variable-name
|
171 | exports.any = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b || !!fn1(x); }, false); }; };
|
172 | /** Given a class, returns a Predicate function that returns true if the object is of that class */
|
173 | exports.is = function (ctor) { return function (obj) {
|
174 | return (obj != null && obj.constructor === ctor) || obj instanceof ctor;
|
175 | }; };
|
176 | /** Given a value, returns a Predicate function that returns true if another value is === equal to the original value */
|
177 | exports.eq = function (value) { return function (other) { return value === other; }; };
|
178 | /** Given a value, returns a function which returns the value */
|
179 | exports.val = function (v) { return function () { return v; }; };
|
180 | function invoke(fnName, args) {
|
181 | return function (obj) { return obj[fnName].apply(obj, args); };
|
182 | }
|
183 | exports.invoke = invoke;
|
184 | /**
|
185 | * Sorta like Pattern Matching (a functional programming conditional construct)
|
186 | *
|
187 | * See http://c2.com/cgi/wiki?PatternMatching
|
188 | *
|
189 | * This is a conditional construct which allows a series of predicates and output functions
|
190 | * to be checked and then applied. Each predicate receives the input. If the predicate
|
191 | * returns truthy, then its matching output function (mapping function) is provided with
|
192 | * the input and, then the result is returned.
|
193 | *
|
194 | * Each combination (2-tuple) of predicate + output function should be placed in an array
|
195 | * of size 2: [ predicate, mapFn ]
|
196 | *
|
197 | * These 2-tuples should be put in an outer array.
|
198 | *
|
199 | * @example
|
200 | * ```
|
201 | *
|
202 | * // Here's a 2-tuple where the first element is the isString predicate
|
203 | * // and the second element is a function that returns a description of the input
|
204 | * let firstTuple = [ angular.isString, (input) => `Heres your string ${input}` ];
|
205 | *
|
206 | * // Second tuple: predicate "isNumber", mapfn returns a description
|
207 | * let secondTuple = [ angular.isNumber, (input) => `(${input}) That's a number!` ];
|
208 | *
|
209 | * let third = [ (input) => input === null, (input) => `Oh, null...` ];
|
210 | *
|
211 | * let fourth = [ (input) => input === undefined, (input) => `notdefined` ];
|
212 | *
|
213 | * let descriptionOf = pattern([ firstTuple, secondTuple, third, fourth ]);
|
214 | *
|
215 | * console.log(descriptionOf(undefined)); // 'notdefined'
|
216 | * console.log(descriptionOf(55)); // '(55) That's a number!'
|
217 | * console.log(descriptionOf("foo")); // 'Here's your string foo'
|
218 | * ```
|
219 | *
|
220 | * @param struct A 2D array. Each element of the array should be an array, a 2-tuple,
|
221 | * with a Predicate and a mapping/output function
|
222 | * @returns {function(any): *}
|
223 | */
|
224 | function pattern(struct) {
|
225 | return function (x) {
|
226 | for (var i = 0; i < struct.length; i++) {
|
227 | if (struct[i][0](x))
|
228 | return struct[i][1](x);
|
229 | }
|
230 | };
|
231 | }
|
232 | exports.pattern = pattern;
|
233 | //# sourceMappingURL=hof.js.map |
\ | No newline at end of file |