diff --git a/.gitignore b/.gitignore index df2f98b..1b71a3b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,8 @@ pids logs results npm-debug.log -node_modules \ No newline at end of file +node_modules + +# Component crap +build +components \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..ffaab35 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,69 @@ +{ + "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.). + "curly" : true, // Require {} for every new block or scope. + "eqeqeq" : true, // Require triple equals i.e. `===`. + "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`. + "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` + "latedef" : true, // Prohibit variable use before definition. + "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. + "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. + "noempty" : true, // Prohibit use of empty blocks. + "nonew" : true, // Prohibit use of constructors for side-effects. + "plusplus" : true, // Prohibit use of `++` & `--`. + "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions. + "undef" : true, // Require all non-global variables be declared before they are used. + "strict" : false, // Require `use strict` pragma in every file. + "trailing" : true, // Prohibit trailing whitespaces. + + "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). + "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. + "debug" : false, // Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // Tolerate use of `== null`. + "es5" : true, // Allow EcmaScript 5 syntax. + "esnext" : false, // Allow ES.next specific features such as `const` and `let`. + "evil" : false, // Tolerate use of `eval`. + "expr" : false, // Tolerate `ExpressionStatement` as Programs. + "funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside. + "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). + "iterator" : false, // Allow usage of __iterator__ property. + "lastsemic" : false, // Tolerat missing semicolons when the it is omitted for the last statement in a one-line block. + "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. + "laxcomma" : false, // Suppress warnings about comma-first coding style. + "loopfunc" : false, // Allow functions to be defined within loops. + "multistr" : false, // Tolerate multi-line strings. + "onecase" : false, // Tolerate switches with just one case. + "proto" : false, // Tolerate __proto__ property. This property is deprecated. + "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. + "scripturl" : false, // Tolerate script-targeted URLs. + "smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignmnent only. + "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. + "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. + "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. + "validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function. + + "browser" : true, // Standard browser globals e.g. `window`, `document`. + "couch" : false, // Enable globals exposed by CouchDB. + "devel" : true, // Allow development statements e.g. `console.log();`. + "dojo" : false, // Enable globals exposed by Dojo Toolkit. + "jquery" : false, // Enable globals exposed by jQuery JavaScript library. + "mootools" : false, // Enable globals exposed by MooTools JavaScript framework. + "node" : true, // Enable globals available when code is running inside of the NodeJS runtime environment. + "nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape. + "prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework. + "rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment. + "wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host. + + "nomen" : false, // Prohibit use of initial or trailing underbars in names. + "onevar" : false, // Allow only one `var` statement per function. + "passfail" : false, // Stop on first error. + "white" : false, // Check against strict whitespace and indentation rules. + + "maxerr" : 100, // Maximum errors before stopping. + "predef" : [ // Extra globals. + "describe", + "it", + "before", + "expect" + ], + "indent" : 4 // Specify indentation spacing +} diff --git a/Gruntfile.js b/Gruntfile.js index a86e5b5..794e3e2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,13 +4,15 @@ module.exports = function (grunt) { jshint: { all: [ "*.js", - "!functional.min.js" + "!functional.min.js", + "!functional.min.client.js" ] }, watch: { files: [ "*.js", - "!functional.min.js" + "!functional.min.js", + "!functional.min.client.js" ], tasks: ["test"] }, @@ -34,11 +36,25 @@ module.exports = function (grunt) { "(c) <%= pkg.author %>\n" + "*/\n" } + }, + "string-replace": { + my_target: { + files: { + "functional.min.client.js": "functional.min.js" + }, + options: { + replacements: [{ + pattern: /λ/g, + replacement: "lambda" + }] + } + } } }); grunt.loadNpmTasks("grunt-contrib-watch"); grunt.loadNpmTasks("grunt-contrib-jshint"); grunt.loadNpmTasks("grunt-contrib-jasmine"); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.registerTask("test", ["jshint", "uglify", "jasmine"]); + grunt.loadNpmTasks("grunt-contrib-uglify"); + grunt.loadNpmTasks("grunt-string-replace"); + grunt.registerTask("test", ["jshint", "uglify", "jasmine", "string-replace"]); }; \ No newline at end of file diff --git a/README.md b/README.md index 978e588..2b49bae 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,16 @@ -# functional.js (λ) [![Build Status](https://travis-ci.org/leecrossley/functional-js.png?branch=master)](https://travis-ci.org/leecrossley/functional-js) [![devDependency Status](https://david-dm.org/leecrossley/functional-js/dev-status.png)](https://david-dm.org/leecrossley/functional-js#info=devDependencies) +# functional.js (λ) [![Build Status](https://travis-ci.org/leecrossley/functional-js.png?branch=master)](https://travis-ci.org/leecrossley/functional-js) [![npm version](https://badge.fury.io/js/functional.js.png)](https://npmjs.org/package/functional.js) [![devDependency Status](https://david-dm.org/leecrossley/functional-js/dev-status.png)](https://david-dm.org/leecrossley/functional-js#info=devDependencies) -functional.js is (go on, guess) a functional js library. It facilitates [currying](http://en.wikipedia.org/wiki/Currying) and [point-free / tacit](http://en.wikipedia.org/wiki/Tacit_programming) programming in JavaScript. + +**functional.js is a functional JavaScript library.** -## Getting started +It facilitates [currying](http://en.wikipedia.org/wiki/Currying) and [point-free / tacit](http://en.wikipedia.org/wiki/Tacit_programming) programming. -### Using npm +### Documentation -``` -npm install functional.js -``` - -To then include functional.min.js in your app: - -``` -var λ = require("functional.js"); -``` - -### Direct dependency - -Download the minified version [here](http://bit.ly/funcmin), reference the js file and λ will become a global variable. - -## Examples - -### Basic λ.curry example - -```javascript -var concatenate = λ.curry(function(word1, word2) { - return word1 + " " + word2; -}); - -var concatenateHello = concatenate("Hello"); -var result = concatenateHello("World"); - -expect(result).toEqual("Hello World"); -``` - -### Another λ.curry example +Visit [functionaljs.com](http://functionaljs.com/) for the full documentation. -```javascript -var add = λ.curry(function(arg1, arg2, arg3) { - return arg1 + arg2 + arg3; -}); - -var add3 = add(3), - add5 = add3(2); - -expect(add(3)(2)(1)).toEqual(6); -expect(add3(2, 1)).toEqual(6); -expect(add3(2)(1)).toEqual(6); -expect(add5(1)).toEqual(6); -``` - -### Extending arity with λ.curry example +### Quick example ```javascript var add = λ.curry(function(arg1, arg2) { @@ -61,123 +19,10 @@ var add = λ.curry(function(arg1, arg2) { var add3 = add(3); -expect(add(1, 2, 3)).toEqual(6); -expect(add3(1, 2, 3, 4, 5)).toEqual(18); +add(1, 2, 3); // => 6 +add3(1, 2, 3, 4, 5); // => 18 ``` -### Curried λ.each example - -```javascript -var result = [], - items = ["f", "u", "n", "c"]; - -var addTo = function (item) { - return result.push(item); -}; - -var addToResult = λ.each(addTo); -expect(typeof (addToResult)).toEqual("function"); - -addToResult(items); -expect(result).toEqual(["f", "u", "n", "c"]); -``` - -### Curried λ.map example - -```javascript -var items = [1, 2, 3]; - -var doubleUp = function (number) { - return number * 2; -}; - -var doubleMap = λ.map(doubleUp); -expect(typeof (doubleMap)).toEqual("function"); - -var result = doubleMap(items); -expect(result).toEqual([2, 4, 6]); -``` - -### Curried λ.reduce example - -```javascript -var items = [1, 2, 3]; - -var multiply = function (arg1, arg2) { - return arg1 * arg2; -}; - -var multiplyReduceFrom1 = λ.reduce(multiply, 1); -expect(typeof (multiplyReduceFrom1)).toEqual("function"); - -var result = multiplyReduceFrom1(items); -expect(result).toEqual(6); -``` - -### Curried λ.any example - -```javascript -var items1 = [1, 2, 3], - items2 = [1, 3, 5]; - -var even = function (item) { - return item % 2 === 0; -}; - -var anyEven = λ.any(even); - -expect(anyEven(items1)).toBeTruthy(); -expect(anyEven(items2)).not.toBeTruthy(); -``` - -### Curried λ.select example - -```javascript -var items = [1, 2, 3, 4, 5]; - -var even = function (item) { - return item % 2 === 0; -}; -var odd = function (item) { - return item % 2 !== 0; -}; - -var selectEven = λ.select(even); -var selectOdd = λ.select(odd); - -expect(selectEven(items)).toEqual([2, 4]); -expect(selectOdd(items)).toEqual([1, 3, 5]); -``` - -### Multiple λ.compose example - -```javascript -var e = function (a) { - return "hello " + a; -}; -var f = function (a) { - return a + 1; -}; -var g = function (a) { - return a * 100; -}; -var composed = λ.compose(e, f, g); - -expect(composed(2)).toEqual("hello 201"); -``` - -### Curried λ.partition example - -```javascript -var items = [7, 6, 5, 4, 3, 2, 1]; - -var even = function (item) { - return item % 2 === 0; -}; - -var partitionEven = λ.partition(even); - -var result = partitionEven(items); +## License -expect(result).toEqual([[6, 4, 2], [7, 5, 3, 1]]); -``` \ No newline at end of file +[MIT License](http://ilee.mit-license.org) diff --git a/component.json b/component.json new file mode 100644 index 0000000..bcd6ee2 --- /dev/null +++ b/component.json @@ -0,0 +1,26 @@ +{ + "name": "functional.js", + "author": "Lee Crossley (http://ilee.co.uk/)", + "description": "A functional JavaScript library that facilitates currying and point-free programming", + "homepage": "http://functionaljs.com", + "version": "0.4.11", + "keywords": [ + "functional", + "curry", + "arity", + "compose", + "iterator", + "collection", + "lambda", + "underscore" + ], + "repo": "leecrossley/functional-js", + "main": "functional.min.client.js", + "scripts": [ + "functional.min.client.js", + "functional.min.js", + "functional.js" + ], + "version": "1.6.0", + "license": "MIT" +} diff --git a/functional.js b/functional.js index 2fae6f2..5a3f429 100644 --- a/functional.js +++ b/functional.js @@ -1,20 +1,33 @@ var λ = (function () { - var λ = {}; + "use strict"; + var λ = {}, hardReturn = "hardReturn;"; - λ.curry = function (func) { - if (!func || typeof (func) !== "function") { - throw "λ Error: No function to curry"; + var sliceArgs = function (args) { + return args.length > 0 ? [].slice.call(args, 0) : []; + }; + + λ.isFunction = function (obj) { + return typeof (obj) === "function"; + }; + + var checkFunction = function (func) { + if (!λ.isFunction(func)) { + throw "λ Error: Invalid function"; } + }; + + λ.curry = function (func) { + checkFunction(func); return function inner() { - var _args = arguments.length >= 1 ? [].slice.call(arguments, 0) : []; + var _args = sliceArgs(arguments); if (_args.length === func.length) { return func.apply(null, _args); } else if (_args.length > func.length) { var initial = func.apply(null, _args); - return λ.reduce(func, initial, _args.slice(func.length)); + return λ.fold(func, initial, _args.slice(func.length)); } else { return function() { - var args = arguments.length >= 1 ? [].slice.call(arguments, 0) : []; + var args = sliceArgs(arguments); return inner.apply(null, _args.concat(args)); }; } @@ -22,77 +35,129 @@ var λ = (function () { }; λ.each = λ.curry(function (iterator, items) { - if (!items || typeof (iterator) !== "function") { + checkFunction(iterator); + if (!λ.exists(items) || !λ.isArray(items)) { return; } - for (var i = 0; i < items.length; i++) { - iterator(items[i]); + for (var i = 0; i < items.length; i += 1) { + if (iterator.call(null, items[i], i) === hardReturn) { + return; + } } }); λ.map = λ.curry(function (iterator, items) { - var mapped = [], - mapEach; - if (!items || typeof (iterator) !== "function") { - return; - } - mapEach = λ.each(function () { + checkFunction(iterator); + var mapped = []; + λ.each(function () { mapped.push(iterator.apply(null, arguments)); - }); - mapEach(items); + }, items); return mapped; }); - λ.reduce = λ.reducel = λ.curry(function (iterator, initial, items) { - var cumulate = initial, - reduceEach; - if (!items || typeof (iterator) !== "function") { - return; - } - reduceEach = λ.each(function (item) { + λ.fold = λ.foldl = λ.curry(function (iterator, cumulate, items) { + checkFunction(iterator); + λ.each(function (item) { cumulate = iterator.call(null, cumulate, item); - }); - reduceEach(items); + }, items); return cumulate; }); - λ.any = λ.curry(function (iterator, items) { - var anyEach, - isAny = false; - if (typeof (iterator) !== "function") { - throw "λ Error: Invalid function"; - } - anyEach = λ.each(function (item) { + λ.reduce = λ.reducel = λ.foldll = λ.curry(function (iterator, items) { + checkFunction(iterator); + var cumulate = items[0]; + items.shift(); + return λ.fold(iterator, cumulate, items); + }); + + λ.clone = function (items) { + var clone = []; + λ.each(function (item) { + clone.push(item); + }, items); + return clone; + }; + + λ.first = λ.curry(function (iterator, items) { + checkFunction(iterator); + var first; + λ.each(function (item) { + if (iterator.call(null, item)) { + first = item; + return hardReturn; + } + }, items); + return first; + }); + + λ.last = λ.curry(function (iterator, items) { + var itemsClone = λ.clone(items); + return λ.first(iterator, itemsClone.reverse()); + }); + + λ.every = λ.all = λ.curry(function (iterator, items) { + checkFunction(iterator); + var isEvery = true; + λ.each(function (item) { + if (!iterator.call(null, item)) { + isEvery = false; + return hardReturn; + } + }, items); + return isEvery; + }); + + λ.any = λ.contains = λ.curry(function (iterator, items) { + checkFunction(iterator); + var isAny = false; + λ.each(function (item) { if (iterator.call(null, item)) { isAny = true; - return; + return hardReturn; } - }); - anyEach(items); + }, items); return isAny; }); - λ.select = λ.curry(function (iterator, items) { - var filtered = [], - filterEach; - if (typeof (iterator) !== "function") { - throw "λ Error: Invalid function"; - } - filterEach = λ.each(function (item) { + λ.select = λ.filter = λ.curry(function (iterator, items) { + checkFunction(iterator); + var filtered = []; + λ.each(function (item) { if (iterator.call(null, item)) { filtered.push(item); } - }); - filterEach(items); + }, items); return filtered; }); + λ.best = λ.curry(function (iterator, items) { + checkFunction(iterator); + var compare = function (arg1, arg2) { + return iterator.call(this, arg1, arg2) ? + arg1 : arg2; + }; + return λ.reduce(compare, items); + }); + + λ.while = λ.curry(function (iterator, items) { + checkFunction(iterator); + var result = []; + λ.each(function (item) { + if (iterator.call(null, item)) { + result.push(item); + } else { + return hardReturn; + } + }, items); + return result; + }); + λ.compose = function (funcs) { - var hasInvalid = λ.any(function (func) { - return typeof (func) !== "function"; + var anyInvalid = λ.any(function (func) { + return !λ.isFunction(func); }); - funcs = arguments.length >= 1 ? [].slice.call(arguments, 0) : []; - if (hasInvalid(funcs)) { + funcs = sliceArgs(arguments); + if (anyInvalid(funcs)) { throw "λ Error: Invalid function to compose"; } return function() { @@ -106,19 +171,77 @@ var λ = (function () { }; λ.partition = λ.curry(function (iterator, items) { - var truthy = [], - falsy = [], - partitionEach; - if (typeof (iterator) !== "function") { - throw "λ Error: Invalid function"; - } - partitionEach = λ.each(function (item) { + checkFunction(iterator); + var truthy = [], + falsy = []; + λ.each(function (item) { (iterator.call(null, item) ? truthy : falsy).push(item); - }); - partitionEach(items); + }, items); return [truthy, falsy]; }); + λ.group = λ.curry(function (iterator, items) { + checkFunction(iterator); + var result = {}; + var group; + λ.each(function (item) { + group = iterator.call(null, item); + result[group] = result[group] || []; + result[group].push(item); + }, items); + return result; + }); + + λ.isArray = function (obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; + + λ.toArray = function (obj) { + return λ.map(function (key) { + return [key, obj[key]]; + }, Object.keys(obj)); + }; + + λ.apply = λ.curry(function (func, items) { + var args = []; + if (λ.isArray(func)) { + args = [].slice.call(func, 1); + func = func[0]; + } + return λ.map(function (item) { + return item[func].apply(item, args); + }, items); + }); + + λ.assign = λ.extend = λ.curry(function (obj1, obj2) { + λ.each(function (key) { + obj2[key] = obj1[key]; + }, Object.keys(obj1)); + return obj2; + }); + + λ.prop = function (prop) { + return function (obj) { + return obj[prop]; + }; + }; + + λ.pluck = λ.curry(function (prop, items) { + return λ.map(λ.prop(prop), items); + }); + + λ.exists = function (obj) { + return obj != null; // jshint ignore:line + }; + + λ.truthy = function (obj) { + return λ.exists(obj) && obj !== false; + }; + + λ.falsy = function (obj) { + return !λ.truthy(obj); + }; + return λ; })(); diff --git a/functional.min.client.js b/functional.min.client.js new file mode 100644 index 0000000..80703ee --- /dev/null +++ b/functional.min.client.js @@ -0,0 +1,5 @@ +/*! + functional.js (v0.4.11) 06-08-2014 + (c) Lee Crossley (http://ilee.co.uk/) +*/ +var lambda=function(){"use strict";var a={},b="hardReturn;",c=function(a){return a.length>0?[].slice.call(a,0):[]};a.isFunction=function(a){return"function"==typeof a};var d=function(b){if(!a.isFunction(b))throw"lambda Error: Invalid function"};return a.curry=function(b){return d(b),function e(){var d=c(arguments);if(d.length===b.length)return b.apply(null,d);if(d.length>b.length){var f=b.apply(null,d);return a.fold(b,f,d.slice(b.length))}return function(){var a=c(arguments);return e.apply(null,d.concat(a))}}},a.each=a.curry(function(c,e){if(d(c),a.exists(e)&&a.isArray(e))for(var f=0;f (http://ilee.co.uk/) */ -var λ=function(){var a={};return a.curry=function(b){if(!b||"function"!=typeof b)throw"λ Error: No function to curry";return function c(){var d=arguments.length>=1?[].slice.call(arguments,0):[];if(d.length===b.length)return b.apply(null,d);if(d.length>b.length){var e=b.apply(null,d);return a.reduce(b,e,d.slice(b.length))}return function(){var a=arguments.length>=1?[].slice.call(arguments,0):[];return c.apply(null,d.concat(a))}}},a.each=a.curry(function(a,b){if(b&&"function"==typeof a)for(var c=0;c=1?[].slice.call(arguments,0):[],c(b))throw"λ Error: Invalid function to compose";return function(){var c=arguments,d=a.each(function(a){c=[a.apply(null,c)]});return d(b.reverse()),c[0]}},a.partition=a.curry(function(b,c){var d,e=[],f=[];if("function"!=typeof b)throw"λ Error: Invalid function";return d=a.each(function(a){(b.call(null,a)?e:f).push(a)}),d(c),[e,f]}),a}();"undefined"!=typeof exports&&("undefined"!=typeof module&&module.exports&&(exports=module.exports=λ),exports.λ=λ); \ No newline at end of file +var λ=function(){"use strict";var a={},b="hardReturn;",c=function(a){return a.length>0?[].slice.call(a,0):[]};a.isFunction=function(a){return"function"==typeof a};var d=function(b){if(!a.isFunction(b))throw"λ Error: Invalid function"};return a.curry=function(b){return d(b),function e(){var d=c(arguments);if(d.length===b.length)return b.apply(null,d);if(d.length>b.length){var f=b.apply(null,d);return a.fold(b,f,d.slice(b.length))}return function(){var a=c(arguments);return e.apply(null,d.concat(a))}}},a.each=a.curry(function(c,e){if(d(c),a.exists(e)&&a.isArray(e))for(var f=0;f (http://ilee.co.uk/)", - "description": "functional.js is (go on, guess) a functional js library. It facilitates currying and point-free / tacit programming in JavaScript. Minified version here: http://bit.ly/funcmin", - "version": "0.1.8", - "main": "functional.min.js", - "keywords": [ - "functional", - "functionaljs", - "functional js", - "functional-js", - "lambda", - "λ", - "array", - "underscore" - ], - "contributors": [ - { - "name": "Lee Crossley", - "email": "leee@hotmail.co.uk" - }, - { - "name": "Ryan Roberts", - "email": "ryansroberts@gmail.com" - } - ], - "scripts": { - "test": "grunt test" + "name": "functional.js", + "author": "Lee Crossley (http://ilee.co.uk/)", + "description": "A functional JavaScript library that facilitates currying and point-free programming", + "homepage": "http://functionaljs.com", + "version": "0.4.11", + "main": "functional.min.js", + "keywords": [ + "functional", + "curry", + "arity", + "compose", + "iterator", + "collection", + "lambda", + "underscore" + ], + "contributors": [ + { + "name": "Lee Crossley", + "email": "leee@hotmail.co.uk" }, - "repository": { - "type": "git", - "url": "git@github.com:leecrossley/functional-js.git" - }, - "engines": { - "node": ">= 0.8.x" - }, - "devDependencies": { - "grunt": "~0.4.1", - "grunt-shell": "~0.3.1", - "grunt-contrib-watch": "~0.5.1", - "grunt-contrib-jshint": "~0.6.2", - "grunt-contrib-jasmine": "~0.5.1", - "grunt-contrib-uglify": "~0.2.2", - "grunt-cli": "~0.1.9" + { + "name": "Ryan Roberts", + "email": "ryansroberts@gmail.com" } + ], + "scripts": { + "test": "grunt test" + }, + "repository": { + "type": "git", + "url": "git@github.com:leecrossley/functional-js.git" + }, + "engines": { + "node": ">= 0.8.x" + }, + "devDependencies": { + "grunt": "~0.4.x", + "grunt-contrib-watch": "~0.6.x", + "grunt-contrib-jshint": "~0.10.x", + "grunt-contrib-jasmine": "~0.7.x", + "grunt-contrib-uglify": "~0.5.x", + "grunt-cli": "~0.1.x", + "grunt-string-replace": "~0.2.x" + } } diff --git a/spec.js b/spec.js index 2174e23..79b2de7 100644 --- a/spec.js +++ b/spec.js @@ -1,3 +1,5 @@ +/*global λ*/ + describe("functional", function() { it("should have a global λ object", function() { @@ -12,8 +14,8 @@ describe("functional", function() { λ.curry("I am a string"); }; - expect(result1).toThrow("λ Error: No function to curry"); - expect(result2).toThrow("λ Error: No function to curry"); + expect(result1).toThrow("λ Error: Invalid function"); + expect(result2).toThrow("λ Error: Invalid function"); }); it("should λ.curry a string concatenation function", function() { @@ -74,12 +76,26 @@ describe("functional", function() { }; var addToResult = λ.each(addTo); - expect(typeof (addToResult)).toEqual("function"); + expect(λ.isFunction(addToResult)).toBeTruthy(); addToResult(items); expect(result).toEqual(["f", "u", "n", "c"]); }); + it("should handle null param to λ.each", function() { + var nothing = function (item) { + return item; + }; + + var doNothing = λ.each(nothing); + + var result = function () { + doNothing(null); + }; + + expect(result).not.toThrow(); + }); + it("should be able to double numbers in an array using λ.map", function() { var items = [1, 2, 3]; @@ -100,14 +116,15 @@ describe("functional", function() { }; var doubleMap = λ.map(doubleUp); - expect(typeof (doubleMap)).toEqual("function"); + expect(λ.isFunction(doubleUp)).toBeTruthy(); var result = doubleMap(items); expect(result).toEqual([2, 4, 6]); }); - it("should be able to use λ.reduce or λ.reducel", function() { + it("should be able to use λ.reduce, λ.reducel or λ.foldll", function() { expect(λ.reduce).toEqual(λ.reducel); + expect(λ.reduce).toEqual(λ.foldll); }); it("should be able to cumulate an array of numbers using λ.reduce", function() { @@ -117,7 +134,7 @@ describe("functional", function() { return arg1 + arg2; }; - var result = λ.reduce(add, 0, items); + var result = λ.reduce(add, items); expect(result).toEqual(6); }); @@ -128,7 +145,7 @@ describe("functional", function() { return arg1 + arg2; }; - var result = λ.reduce(concatenate, "", items); + var result = λ.reduce(concatenate, items); expect(result).toEqual("func"); }); @@ -139,13 +156,80 @@ describe("functional", function() { return arg1 * arg2; }; - var multiplyReduceFrom1 = λ.reduce(multiply, 1); - expect(typeof (multiplyReduceFrom1)).toEqual("function"); + var multiplyReduce = λ.reduce(multiply); + expect(λ.isFunction(multiplyReduce)).toBeTruthy(); - var result = multiplyReduceFrom1(items); + var result = multiplyReduce(items); expect(result).toEqual(6); }); + it("should be able to use λ.fold or λ.foldl", function() { + expect(λ.fold).toEqual(λ.foldl); + }); + + it("should be able to λ.curry λ.fold", function() { + var items = [1, 2, 3]; + + var multiply = function (arg1, arg2) { + return arg1 * arg2; + }; + + var multiplyFoldFrom10 = λ.fold(multiply, 10); + expect(λ.isFunction(multiplyFoldFrom10)).toBeTruthy(); + + var result = multiplyFoldFrom10(items); + expect(result).toEqual(60); + }); + + it("should be able to λ.curry λ.best", function() { + var items = [1, -4, 2, 3]; + + var biggest = function (arg1, arg2) { + return arg1 > arg2; + }; + + var smallest = function (arg1, arg2) { + return arg1 < arg2; + }; + + var biggestAndBest = λ.best(biggest); + var bestSmallest = λ.best(smallest); + + expect(λ.isFunction(biggestAndBest)).toBeTruthy(); + expect(λ.isFunction(bestSmallest)).toBeTruthy(); + + expect(biggestAndBest(items)).toEqual(3); + expect(bestSmallest(items)).toEqual(-4); + }); + + it("should be able to λ.curry λ.best to get the longest word", function() { + var words = ["simply", "the", "best"]; + + var longest = λ.best(function (arg1, arg2) { + return arg1.length > arg2.length; + }); + + expect(λ.isFunction(longest)).toBeTruthy(); + + expect(longest(words)).toEqual("simply"); + }); + + it("should be able to λ.curry λ.while to get even numbers until odd", function() { + var even = function (item) { + return item % 2 === 0; + }; + + var whileEven = λ.while(even); + + expect(whileEven([2])).toEqual([2]); + expect(whileEven([2, 4, 5, 6])).toEqual([2, 4]); + expect(whileEven([1, 4, 6, 8])).toEqual([]); + }); + + it("should be able to use λ.any or λ.contains", function() { + expect(λ.any).toEqual(λ.contains); + }); + it("should be able to λ.curry λ.any", function() { var items1 = [1, 2, 3], items2 = [1, 3, 5]; @@ -155,9 +239,12 @@ describe("functional", function() { }; var anyEven = λ.any(even); + var containsEven = λ.contains(even); expect(anyEven(items1)).toBeTruthy(); + expect(containsEven(items1)).toBeTruthy(); expect(anyEven(items2)).not.toBeTruthy(); + expect(containsEven(items2)).not.toBeTruthy(); }); it("should be able to λ.curry λ.select", function() { @@ -177,6 +264,71 @@ describe("functional", function() { expect(selectOdd(items)).toEqual([1, 3, 5]); }); + it("should be able to λ.clone an array and keep independence", function() { + var items = [5, 4, 3, 2, 1]; + + var clonedItems = λ.clone(items); + + expect(clonedItems).toEqual(items); + items = []; + expect(clonedItems).not.toEqual(items); + }); + + it("should be able to λ.curry λ.first", function() { + var items = [5, 4, 3, 2, 1]; + + var even = function (item) { + return item % 2 === 0; + }; + var odd = function (item) { + return item % 2 !== 0; + }; + + var firstEven = λ.first(even); + var firstOdd = λ.first(odd); + + expect(firstEven(items)).toEqual(4); + expect(firstOdd(items)).toEqual(5); + }); + + it("should be able to λ.curry λ.last", function() { + var items = [5, 4, 3, 2, 1]; + + var even = function (item) { + return item % 2 === 0; + }; + var odd = function (item) { + return item % 2 !== 0; + }; + + var lastEven = λ.last(even); + var lastOdd = λ.last(odd); + + expect(lastEven(items)).toEqual(2); + expect(lastOdd(items)).toEqual(1); + }); + + it("should be able to λ.curry λ.every", function() { + var items = [2, 4, 6, 8]; + + var even = function (item) { + return item % 2 === 0; + }; + var odd = function (item) { + return item % 2 !== 0; + }; + + var everyEven = λ.every(even); + var everyOdd = λ.every(odd); + + expect(everyEven(items)).toEqual(true); + expect(everyOdd(items)).toEqual(false); + }); + + it("should be able to use λ.every or λ.all", function() { + expect(λ.every).toEqual(λ.all); + }); + it("should throw an error attempting to λ.compose anything that isn't a function", function() { var f = function (a) { return "hello " + a; @@ -235,7 +387,7 @@ describe("functional", function() { var even = function (item) { return item % 2 === 0; }; - + var partitionEven = λ.partition(even); var result = partitionEven(items); @@ -243,4 +395,224 @@ describe("functional", function() { expect(result).toEqual([[6, 4, 2], [7, 5, 3, 1]]); }); -}); \ No newline at end of file + it("should be able to λ.curry λ.group", function() { + var items = ["Lee", "Ryan", "Leona", "Sarah", "Rob", "Liam"]; + + var firstLetter = function (item) { + return item.charAt(0); + }; + + var groupFirstLetter = λ.group(firstLetter); + + var result = groupFirstLetter(items); + + expect(result).toEqual({"L": [ "Lee", "Leona", "Liam" ], + "R": [ "Ryan", "Rob" ], "S": [ "Sarah" ]}); + }); + + it("should be able to λ.curry λ.pluck", function() { + var items = [{ + "p1": "abc", + "p2": false, + "p3": 123 + }, { + "p1": "cab", + "p2": true, + "p3": 312 + },{ + "p1": "bca", + "p2": false, + "p3": 231 + }]; + + var pluck1 = λ.pluck("p1"); + var result1 = pluck1(items); + var pluck2 = λ.pluck("p2"); + var result2 = pluck2(items); + + expect(result1).toEqual(["abc", "cab", "bca"]); + expect(result2).toEqual([false, true, false]); + }); + + it("should convert an object to an array", function() { + var obj = { + "p1": "abc", + "p2": false, + "p3": null + }; + + var result = λ.toArray(obj); + + expect(result).toEqual([["p1", "abc"], ["p2", false], ["p3", null]]); + expect(λ.isArray(obj)).toBeFalsy(); + expect(λ.isArray(result)).toBeTruthy(); + }); + + it("should be able to λ.curry λ.apply", function() { + var items = ["Hello", "World"]; + + var applyCase = λ.apply("toUpperCase"); + + var result = applyCase(items); + + expect(result).toEqual(["HELLO", "WORLD"]); + }); + + it("should be able to λ.curry λ.apply with additional argument", function() { + var items = ["Hello", "World"]; + + var applyIndexOf = λ.apply(["indexOf", "o"]); + + var result = applyIndexOf(items); + + expect(result).toEqual([4, 1]); + }); + + it("should be able to λ.curry λ.apply with multiple arguments", function() { + var items = ["Hello", "World"]; + + var applyIndexOf = λ.apply(["substring", "1", "4"]); + + var result = applyIndexOf(items); + + expect(result).toEqual(["ell", "orl"]); + }); + + it("should be able to use λ.assign or λ.extend", function() { + expect(λ.assign).toEqual(λ.extend); + }); + + it("should be able to do a basic λ.assign", function() { + var obj1 = { + prop1: "obj1prop1", + prop2: "obj1prop2" + }; + var obj2 = { + prop2: "obj2prop2", + prop3: "obj2prop3" + }; + + var result = λ.assign(obj1, obj2); + + expect(result).toEqual({ + prop1: "obj1prop1", + prop2: "obj1prop2", + prop3: "obj2prop3" + }); + }); + + it("should be able to λ.curry λ.assign and extend the arity", function() { + var obj1 = { + prop1: "obj1prop1", + prop2: "obj1prop2" + }; + var obj2 = { + prop2: "obj2prop2", + prop3: "obj2prop3", + prop4: "obj2prop4" + }; + var obj3 = { + prop4: "obj3prop4", + prop5: "obj3prop5" + }; + + var assignToObj1 = λ.assign(obj1); + var result1 = assignToObj1(obj2, obj3); + + var result2 = λ.assign(obj1, obj2, obj3); + + expect(result1).toEqual({ + prop1: "obj1prop1", + prop2: "obj1prop2", + prop3: "obj2prop3", + prop4: "obj2prop4", + prop5: "obj3prop5" + }); + expect(result1).toEqual(result2); + }); + + it("should have correct return values for λ.exists", function() { + expect(λ.exists(undefined)).toBeFalsy(); + expect(λ.exists(null)).toBeFalsy(); + + expect(λ.exists(1)).toBeTruthy(); + expect(λ.exists(-1)).toBeTruthy(); + expect(λ.exists(0)).toBeTruthy(); + expect(λ.exists("abc")).toBeTruthy(); + expect(λ.exists("")).toBeTruthy(); + expect(λ.exists(Number.MAX_VALUE)).toBeTruthy(); + expect(λ.exists(Number.MIN_VALUE)).toBeTruthy(); + expect(λ.exists(NaN)).toBeTruthy(); + expect(λ.exists(0144)).toBeTruthy(); + expect(λ.exists(0xFF)).toBeTruthy(); + expect(λ.exists(0.1)).toBeTruthy(); + expect(λ.exists(-0.1)).toBeTruthy(); + expect(λ.exists(3e5)).toBeTruthy(); + expect(λ.exists(true)).toBeTruthy(); + expect(λ.exists(false)).toBeTruthy(); + expect(λ.exists(Infinity)).toBeTruthy(); + expect(λ.exists(Number.POSITIVE_INFINITY)).toBeTruthy(); + expect(λ.exists(Number.NEGATIVE_INFINITY)).toBeTruthy(); + expect(λ.exists(new Date())).toBeTruthy(); + expect(λ.exists([])).toBeTruthy(); + expect(λ.exists({})).toBeTruthy(); + expect(λ.exists(function() { })).toBeTruthy(); + }); + + it("should have correct return values for λ.truthy", function() { + expect(λ.truthy(undefined)).toBeFalsy(); + expect(λ.truthy(null)).toBeFalsy(); + expect(λ.truthy(false)).toBeFalsy(); + + expect(λ.truthy(1)).toBeTruthy(); + expect(λ.truthy(-1)).toBeTruthy(); + expect(λ.truthy(0)).toBeTruthy(); + expect(λ.truthy("abc")).toBeTruthy(); + expect(λ.truthy("")).toBeTruthy(); + expect(λ.truthy(Number.MAX_VALUE)).toBeTruthy(); + expect(λ.truthy(Number.MIN_VALUE)).toBeTruthy(); + expect(λ.truthy(NaN)).toBeTruthy(); + expect(λ.truthy(0144)).toBeTruthy(); + expect(λ.truthy(0xFF)).toBeTruthy(); + expect(λ.truthy(0.1)).toBeTruthy(); + expect(λ.truthy(-0.1)).toBeTruthy(); + expect(λ.truthy(3e5)).toBeTruthy(); + expect(λ.truthy(true)).toBeTruthy(); + expect(λ.truthy(Infinity)).toBeTruthy(); + expect(λ.truthy(Number.POSITIVE_INFINITY)).toBeTruthy(); + expect(λ.truthy(Number.NEGATIVE_INFINITY)).toBeTruthy(); + expect(λ.truthy(new Date())).toBeTruthy(); + expect(λ.truthy([])).toBeTruthy(); + expect(λ.truthy({})).toBeTruthy(); + expect(λ.truthy(function() { })).toBeTruthy(); + }); + + it("should have correct return values for λ.falsy", function() { + expect(λ.falsy(undefined)).toBeTruthy(); + expect(λ.falsy(null)).toBeTruthy(); + expect(λ.falsy(false)).toBeTruthy(); + + expect(λ.falsy(1)).toBeFalsy(); + expect(λ.falsy(-1)).toBeFalsy(); + expect(λ.falsy(0)).toBeFalsy(); + expect(λ.falsy("abc")).toBeFalsy(); + expect(λ.falsy("")).toBeFalsy(); + expect(λ.falsy(Number.MAX_VALUE)).toBeFalsy(); + expect(λ.falsy(Number.MIN_VALUE)).toBeFalsy(); + expect(λ.falsy(NaN)).toBeFalsy(); + expect(λ.falsy(0144)).toBeFalsy(); + expect(λ.falsy(0xFF)).toBeFalsy(); + expect(λ.falsy(0.1)).toBeFalsy(); + expect(λ.falsy(-0.1)).toBeFalsy(); + expect(λ.falsy(3e5)).toBeFalsy(); + expect(λ.falsy(true)).toBeFalsy(); + expect(λ.falsy(Infinity)).toBeFalsy(); + expect(λ.falsy(Number.POSITIVE_INFINITY)).toBeFalsy(); + expect(λ.falsy(Number.NEGATIVE_INFINITY)).toBeFalsy(); + expect(λ.falsy(new Date())).toBeFalsy(); + expect(λ.falsy([])).toBeFalsy(); + expect(λ.falsy({})).toBeFalsy(); + expect(λ.falsy(function() { })).toBeFalsy(); + }); + +});