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 (λ) [](https://travis-ci.org/leecrossley/functional-js) [](https://david-dm.org/leecrossley/functional-js#info=devDependencies)
+# functional.js (λ) [](https://travis-ci.org/leecrossley/functional-js) [](https://npmjs.org/package/functional.js) [](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();
+ });
+
+});