diff --git a/Select.js b/Select.js deleted file mode 100644 index cf8cfff..0000000 --- a/Select.js +++ /dev/null @@ -1,183 +0,0 @@ -'use strict'; -/** -* Return a function based on a condition. -* Functional alternative to switch-case. -* @projectname Select-Return -* @version 1.0.0 -* @author Muthu Kumar (MKRhere) -*/ - -/** - * Creates a SelectValue instance with a value and optional resolve function. - * Created internally from SelectIterable constructor, and not exported. - * @class SelectValue - */ -class SelectValue { - /** - * @param {any} value - the input value - * @param {function} resolve - optional resolve function - * @constructs SelectIterable - */ - constructor(value, resolve) { - this.value = value; - if (resolve) this.resolve = (...args) => resolve(...args, value); - } - /** - * Default resolve prototype. Returns null when called. - * Used in case a resolve is never set. - * @returns {object} null - * @memberof SelectValue - */ - resolve() { - return null; - } -} - -/** -* Creates a SelectIterable instance from an array. -* Created internally from Select.prototype.for, and not exported. -* @class SelectIterable -* @param {array} values - array created from Select.prototype.for -* @param {Array} tests - array of { test, consequent } objects -* @param {function} tests[].test - test function -* @param {function} tests[].consequent - consequent function -* @constructs SelectIterable -*/ -class SelectIterable { - constructor(values, conditionals) { - this.values = values.map(value => value instanceof SelectValue ? value : new SelectValue(value)); - this.conditionals = conditionals; - } - /** - * Accepts a test and consequent function each and returns a new - * SelectIterable instance. - * SelectIterable.prototype.for works a little differently than - * Select.prototype.for, by accumulating the tests and resolving all the - * values when .resolve() is called. - * @param {callback} predicate A test callback function. - * @callback predicate - * @param {any} value The Selected value. - * @returns {boolean} The Boolean result of the test. - * @param {callback} consequent consequent callback function - * @callback consequent - * @param {Array} args An arbitrary Array of arguments. - * @param {any} value The Selected value. - * @returns {any} - * @returns {SelectIterable} an instance of SelectIterable - * @memberof SelectIterable - */ - if(predicate, consequent) { - return new SelectIterable(this.values, [ - ...this.conditionals, - {predicate, consequent} - ]); - } - forField(...pairs) { - var pair; - var callback; - var even; - pairs.forEach((pair, index) => { - even = index % 2 === 0; - pair = this.conditionals[this.conditionals.length]; - if (pair.length === 2) { - this.conditionals.push({}); - }; - callback = conditionals[loc]; - if (typeof callback === "object") { - if (Array.isArray(callback)) { - pair.predicate = callback[0]; - pair.consequent = callback[0]; - } else { - Object.assign(pair, { - predicate: callback.predicate, - consequent: callback.consequent - }); - } - continue; - } - if (even) pair.predicate = callback; - if (!even) pair.consequent = callback; - }, this); - return new SelectIterable( - this.values, - [...this.conditionals, ...conditionals] - ); - } - subset(value, consequent) { - return this.if(this.value.every(element => element in value)) - } - superset(value, consequent) { - return this.if(value.every(element => element in this.value)); - } -} -SelectIterable.prototype.resolve = function (...args) { - return this.values.map(item => { - const resolver = this.conditionals.find(conditional => - conditional.predicate(item.value) - ? conditional.consequent - : null - ); - return resolver - ? resolver.consequent(...args, this.value) - : null; - }, this); -}; - -/** - * Creates a new Select instance. - * @class Select - * @extends {SelectValue} - */ -class Select extends SelectValue { - /** - * @param {any|array} value - the value or array of values to check against - * @param {function} resolve - optional resolve function - * @constructs Select - */ - constructor(value, resolve) { - super(value, resolve); - this.iterable = typeof value === "object" && Symbol.iterator in value; - } - /** - * Accepts a test and consequent function each and returns a new - * Select or SelectIterable instance. - * @param {callback} predicate A test callback function. - * @callback predicate - * @param {any} value The Selected value. - * @returns {boolean} The Boolean result of the test. - * @param {callback} consequent consequent callback function - * @callback consequent - * @param {Array} args An arbitrary Array of arguments. - * @param {any} value The Selected value. - * @returns {any} - * @returns {(Select|SelectIterable)} - Returns a SelectIterable instance - * if value was array, or a Select instance otherwise - * @memberof Select - */ - if(predicate, consequent) { - if (this.iterable) { - /* If the value passed to the constructor is an - iterable, return a new SelectIterable using the - iterable and { test, consequent } pair.*/ - return new SelectIterable( - Array.from(this.value), - [{ predicate, consequent }] - ); - } - if (predicate(this.value)) { - return new Select(this.value, consequent); - } - /* If the test doesn't pass, just pass the Select - instance along the chain until a test passes, - or .resolve() is called */ - return this; - } - is(value, consequent) { - return this.if(value === this.value, consequent); - } - isNot(value, consequent) { - return this.if(value !== this.value, consequent) - } -} - -module.exports = Select; diff --git a/index.js b/index.js new file mode 100644 index 0000000..144eb74 --- /dev/null +++ b/index.js @@ -0,0 +1,182 @@ +'use strict'; + +/** + * FUSE + * FunctionSelect: Return a function based on a condition. + * @version 0.10.0 + * @author Muthu Kumar (MKRhere) + */ + +/** + * @callback predicate + * @param {any} value The selected Fuse value + * @returns {boolean} The Boolean result of the test + */ + +/** + * @callback consequent + * @param {any} value The selected Fuse value + * @param {...any} args An arbitrary array of arguments + * @returns {any} + */ + +/** + * Creates a FuseItem instance with a value and optional resolve function. + * FuseIterable constructor uses it internally, and Fuse extends FuseItem. + * Not exposed. + * @class FuseItem + */ +class FuseItem { + /** + * @param {any} value The input value + * @param {function} resolve Optional resolve function + * @constructs FuseItem + */ + constructor(value, resolve) { + this.value = value; + if (resolve) { + this.resolve = (...args) => resolve(this.value, ...args); + this.resolved = true; + } + } + + /** + * Fallback resolve prototype. Returns null when called. + * Used in case a resolve is never found. + * @returns {null} null + * @memberof FuseItem + */ + resolve() { + return null; + } +} + +/** + * Creates a FuseIterable instance from an array or iterable. + * @class FuseIterable + * @param {iterable} values An iterable expression as the switch + * @param {Array} tests Array of { test, consequent } objects + * @param {function} tests[].predicate Test function + * @param {function} tests[].consequent Consequent function + * @constructs FuseIterable + */ +class FuseIterable { + constructor(values, conditionals) { + this.values = Array.from(values).map(value => + value instanceof FuseItem + ? value + : new FuseItem(value)); + this.conditionals = conditionals || []; + } + + /** + * Accepts a test and consequent function each and returns a new + * FuseIterable instance. + * FuseIterable.prototype.for works a little differently than + * Fuse.prototype.for, by lazy accumulating the tests and + * resolving all the values when .resolve() is called. + * @param {callback} predicate A test callback function + * @param {callback} consequent Consequent callback function + * @returns {FuseIterable} An instance of FuseIterable + * @memberof FuseIterable + */ + on(predicate, consequent) { + return new FuseIterable(this.values, [ + ...this.conditionals, + { predicate, consequent } + ]); + } + + /** + * Accepts a list of tuples as arguments and returns a new + * FuseIterable instance. An alternative to chaining multiple + * FuseIterable.prototype.on methods. + * @memberOf FuseIterable + * @param {...Array.} tuples - + * Array of [ predicate, consequent ] pairs + * @returns {FuseIterable} An instance of FuseIterable + */ + onField(...tuples) { + const conditionals = tuples.map(conditional => + ({ + predicate: conditional[0], + consequent: conditional[1] + })); + return new FuseIterable(this.values, conditionals); + } + + /** + * Accepts parameters during resolve time and passes them along with + * each value into the winning consequent function. + * @param {...any} args Any number of arguments + * @returns {(any|null)} Resolved value or null if it was unresolved + */ + resolve(...args) { + return this.values.map(item => { + const resolver = this.conditionals.find(conditional => + conditional.predicate(item.value) + ? conditional.consequent + : null + ); + return resolver + ? resolver.consequent(item.value, ...args) + : null; + }); + } +} + +/** + * Creates a new Fuse instance. + * @class Fuse + * @extends {FuseItem} + */ +class Fuse extends FuseItem { + + /** + * Accepts a test and consequent function each and returns a new + * Fuse or FuseIterable instance. + * @param {callback} predicate A test callback function + * @param {callback} consequent Consequent callback function + * @returns {Fuse} Returns a new Fuse instance + * @memberof Fuse + */ + on(predicate, consequent) { + + /* If a resolve exists, just pass on the instance + until .resolve() is called */ + if (this.resolved) return this; + + if (predicate(this.value)) return new Fuse(this.value, consequent); + + /* If the test doesn't pass, just pass the Fuse + instance along the chain until a test passes, + or .resolve() is called */ + return this; + } + + /** + * Accepts a value instead of a test function, and checks for strict + * equality with this.value. + * @param {any} value Any value to check against this.value + * @param {function} consequent Consequent callback function + * @returns {Fuse} An instance of Fuse + */ + is(value, consequent) { + return this.on(() => value === this.value, consequent); + } + + + /** + * Accepts a value instead of a test function, and checks for strict + * inequality with this.value. + * @param {any} value Any value to check against this.value + * @param {function} consequent Consequent callback function + * @returns {Fuse} An instance of Fuse + */ + not(value, consequent) { + return this.on(() => value !== this.value, consequent); + } +} + +module.exports = Fuse; +module.exports.FuseIterable = FuseIterable;