diff options
-rw-r--r-- | js/b.js | 122 | ||||
-rw-r--r-- | js/b.min.js | 1 |
2 files changed, 71 insertions, 52 deletions
diff --git a/js/b.js b/js/b.js index 459c79f..5462e93 100644 --- a/js/b.js +++ b/js/b.js @@ -36,12 +36,28 @@ const b = { /** + * Converts a function into a curried function, allowing for partial application. + * A curried function can be partially applied and will return a new function until all arguments are provided. + * @param {Function} fn - The function to curry. + * @returns {Function} - The curried function. + */ + curry: function (fn) { + const curried = (...args) => { + if (args.length >= fn.length) + return fn(...args) + else + return (...rest) => curried(...args, ...rest) + } + return curried + }, + + /** * Composes functions from left to right. - * Takes any number of functions as arguments and returns the result of applying them in sequence. - * @param {...Function} args - The functions to be composed. - * @returns {Function} - A function that applies the composed functions from left to right. + * Takes any number of functions as arguments and returns a function that applies them in sequence to a supplied value. + * @param {...Function} fns - The functions to be composed. + * @returns {Function} - A function that takes an initial value and applies the composed functions from left to right. */ - pipe: (...args) => args.reduce((acc, el) => el(acc)), + pipe: (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value), /** * Composes functions from right to left. @@ -49,92 +65,94 @@ const b = { * @param {...Function} fns - The functions to be composed. * @returns {Function} - A function that applies the composed functions from right to left. */ - compose: (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0], + compose: (...fns) => (...args) => fns.reduceRight((res, fn) => fn(...[].concat(res)), args), /** - * A function that returns its input unmodified. + * A function that returns its input totally and completely unchanged. * @param {*} x - The input value. * @returns {*} - The same input value. */ identity: x => x, /** - * Converts a function into a curried function. - * A curried function can be partially applied and will return a new function until all arguments are provided. - * @param {Function} fn - The function to curry. - * @returns {Function} - The curried function. - */ - curry: (fn) => { - const curried = (...args) => { - if (args.length >= fn.length) - return fn(...args) - else - return (...rest) => curried(...args, ...rest) - } - return curried - }, - - /** * Curried function that matches a regular expression against a string. * @returns {Function} - A curried function that takes a regex and a string and returns the match result. */ - match: () => b.curry((what, s) => s.match(what)), + match: function () { + return this.curry((what, s) => s.match(what)); + }, /** * Curried function that replaces parts of a string based on a regex or substring. * @returns {Function} - A curried function that takes a regex or substring, a replacement, and a string, and returns the replaced string. */ - replace: () => b.curry((what, replacement, s) => s.replace(what, replacement)), + replace: function () { + return this.curry((what, replacement, s) => s.replace(what, replacement)); + }, /** * Curried function that filters an array based on a predicate function. * @returns {Function} - A curried function that takes a predicate function and an array, and returns the filtered array. */ - filter: () => b.curry((f, xs) => xs.filter(f)), + filter: function () { + return this.curry((f, xs) => xs.filter(f)); + }, /** * Curried function that maps a function over an array. * @returns {Function} - A curried function that takes a mapping function and an array, and returns the mapped array. */ - map: () => b.curry((f, xs) => xs.map(f)), + map: function () { + return this.curry((f, xs) => xs.map(f)); + }, /** - * Curried function that recursively maps a function over an array or matrix of any depth. + * Curried function that recursively maps a function over an array or matrix of any shape. * @returns {Function} - A curried function that takes a mapping function and a nested array, and returns the deeply mapped array. */ - deepMap: () => - b.curry(function deepMap(f, xs) { + deepMap: function () { + return this.curry(function deepMap(f, xs) { return Array.isArray(xs) ? xs.map((x) => deepMap(f, x)) : f(xs); - }), + }); + }, }; +// Tests! +// pipe +const addOne = (x) => x + 1; +const double = (x) => x * 2; +console.assert(b.pipe(addOne, double)(3) === 8, 'Test for pipe failed'); + +// compose +console.assert(b.compose(double, addOne)(3) === 8, 'Test for compose failed'); + +// identity +console.assert(b.identity(5) === 5, 'Test for identity failed'); +// curry +const add = (x, y) => x + y; +const curriedAdd = b.curry(add); +console.assert(curriedAdd(3)(4) === 7, 'Test for curry failed'); -// // pipe -// const title = '10 Weird Facts About Dogs'; -// const toLowerCase = (str) => str.toLowerCase(); -// const addHyphens = (str) => str.replace(/\s/g, '-'); -// const reverseText = (str) => (str === '') ? '' : reverseText(str.substr(1)) + str.charAt(0); -// const slug = b.pipe(title, toLowerCase, addHyphens, reverseText); -// console.log('pipe ', slug); +// match +const matchDigits = b.match()(/\d+/g); +console.assert(JSON.stringify(matchDigits('a1b2c3')) === JSON.stringify(['1', '2', '3']), 'Test for match failed'); +// replace +const censor = b.replace()(/badword/g, '****'); +console.assert(censor('This is a badword in a sentence.') === 'This is a **** in a sentence.', 'Test for replace failed'); -// // compose -// const toUpperCase = x => x.toUpperCase(); -// const exclaim = x => `${x}!`; -// const shout = b.compose(exclaim, toUpperCase); -// const ret = shout('send in the clowns'); // "SEND IN THE CLOWNS!" -// console.log('compose ', ret); +// filter +const isEven = (x) => x % 2 === 0; +const filterEvens = b.filter()(isEven); +console.assert(JSON.stringify(filterEvens([1, 2, 3, 4])) === JSON.stringify([2, 4]), 'Test for filter failed'); +// map +const mapDoubles = b.map()(double); +console.assert(JSON.stringify(mapDoubles([1, 2, 3])) === JSON.stringify([2, 4, 6]), 'Test for map failed'); -// // curry family -// const hasLetterR = b.match(/r/g); -// const r1 = hasLetterR('hello world'); -// const r2 = hasLetterR('just j and s and t etc'); -// const r3 = b.filter(hasLetterR, ['rock and roll', 'smooth jazz']); -// const noVowels = b.replace(/[aeiou]/ig); -// const censored = noVowels('*'); -// const r4 = censored('Chocolate Rain'); -// console.log('curry ', r1, r2, r3, r4); \ No newline at end of file +// deepMap +const nestedArray = [1, [2, [3, 4]], [5, 6]]; +console.assert(JSON.stringify(b.deepMap()(double, nestedArray)) === JSON.stringify([2, [4, [6, 8]], [10, 12]]), 'Test for deepMap failed'); \ No newline at end of file diff --git a/js/b.min.js b/js/b.min.js new file mode 100644 index 0000000..1a5647b --- /dev/null +++ b/js/b.min.js @@ -0,0 +1 @@ +'use strict';const b={pipe:(...args)=>args.reduce((acc,el)=>el(acc)),compose:(...fns)=>(...args)=>fns.reduceRight((res,fn)=>[fn.call(null,...res)],args)[0],identity:x=>x,curry:(fn)=>{const curried=(...args)=>{if(args.length>=fn.length){return fn(...args)}else{return(...rest)=>curried(...args,...rest)}}return curried},match:()=>b.curry((what,s)=>s.match(what)),replace:()=>b.curry((what,replacement,s)=>s.replace(what,replacement)),filter:()=>b.curry((f,xs)=>xs.filter(f)),map:()=>b.curry((f,xs)=>xs.map(f)),deepMap:()=>b.curry(function deepMap(f,xs){return Array.isArray(xs)?xs.map((x)=>deepMap(f,x)):f(xs)})}; \ No newline at end of file |