Comparing version 1.2.0 to 1.3.0
var functools = (function(undefined){ | ||
function isArray(el){ | ||
return Object.prototype.toString.call(el) == '[object Array]'; | ||
var slice = Array.prototype.slice; | ||
function isArray(obj){ | ||
return Object.prototype.toString.call(obj) == '[object Array]'; | ||
} | ||
function objectKeys(obj){ | ||
var keys = [], key; | ||
for(key in obj){ | ||
keys.push( key ); | ||
} | ||
return keys; | ||
} | ||
/** | ||
* Function composition implementation | ||
*/ | ||
*/ | ||
function compose(/* functions */){ | ||
var fns = Array.prototype.slice.call(arguments); | ||
var fns = slice.call(arguments); | ||
return function(initialValue){ | ||
return reduce(function(f,g){ | ||
return function(val){ | ||
return function(val){ | ||
return g(f(val)); | ||
@@ -21,4 +31,4 @@ }; | ||
compose.async = function compose_async(/* functions */){ | ||
var fns = Array.prototype.slice.call(arguments); | ||
compose.async = function composeAsync(/* functions */){ | ||
var fns = slice.call(arguments); | ||
return function(initialValue,callback){ | ||
@@ -41,3 +51,3 @@ (function(i,error,value){ | ||
* a list containing the result of applying each fn to the arguments. | ||
* | ||
* | ||
* usage examples; | ||
@@ -48,3 +58,3 @@ * juxt(Number, String, Array)(true) => [1, "true", [true]] | ||
function juxt(/* functions */){ | ||
var fns = arguments.length > 1 || typeof arguments[0] != 'object' ? arguments : arguments[0]; | ||
var fns = arguments.length > 1 || typeof arguments[0] != 'object' ? slice.call(arguments) : arguments[0]; | ||
@@ -54,3 +64,3 @@ return function(){ | ||
return map(function(fn){ | ||
return fn.apply(undefined, args); | ||
return fn.apply(undefined, args); | ||
}, fns); | ||
@@ -62,5 +72,6 @@ }; | ||
juxt.async = function(/* functions */){ | ||
var fns = arguments.length > 1 || typeof arguments[0] != 'object' ? arguments : arguments[0]; | ||
var fns = arguments.length > 1 || typeof arguments[0] != 'object' ? slice.call(arguments) : arguments[0]; | ||
return function(/* args, callback */){ | ||
var args = Array.prototype.slice.call(arguments, 0, arguments.length-1), | ||
var args = slice.call(arguments, 0, arguments.length-1), | ||
callback = arguments[arguments.length-1]; | ||
@@ -79,3 +90,3 @@ | ||
function curry(fn){ | ||
var args = Array.prototype.slice.call(arguments,1), | ||
var args = slice.call(arguments,1), | ||
len = fn.length; | ||
@@ -91,4 +102,5 @@ return (function(){ | ||
*/ | ||
function each(fn,iterable){ | ||
for(var i = -1, len=iterable.length; ++i < len; ){ | ||
function each(fn, iterable){ | ||
var i, len; | ||
for(i = -1, len=iterable.length; ++i < len; ){ | ||
fn(iterable[i],i,iterable); | ||
@@ -99,6 +111,19 @@ }; | ||
each.async = function(fn, iterable, callback){ | ||
(function next(i, error){ | ||
if( error || i >= iterable.length ){ | ||
callback && callback(error); | ||
return; | ||
} | ||
fn(iterable[i], i, iterable, partial(next, [i+1])); | ||
}(0)); | ||
}; | ||
/** | ||
* Apply 'fn' to every element of 'iterable', returning those elements for which fn returned a true value. | ||
*/ | ||
function filter(fn,iterable){ | ||
function filter(fn, iterable){ | ||
var accumulation = []; | ||
@@ -111,3 +136,3 @@ for(var i = -1, len=iterable.length; ++i < len; ){ | ||
filter.async = function filter_async(fn, iterable, callback){ | ||
filter.async = function filterAsync(fn, iterable, callback){ | ||
var accumulation = []; | ||
@@ -126,3 +151,3 @@ (function(i,ptest){ | ||
})(0); | ||
} | ||
}; | ||
@@ -137,6 +162,6 @@ /** | ||
clone = Array.prototype.slice.call(iterable, 0); | ||
clone = slice.call(iterable, 0); | ||
i = -1; | ||
len = clone.length; | ||
while(++i<len){ | ||
@@ -147,3 +172,3 @@ clone[i] = fn(clone[i], i, clone); | ||
} else if(typeof iterable == 'object') { | ||
clone = {}; | ||
@@ -160,11 +185,11 @@ | ||
map.async = function map_async(fn, iterable, callback){ | ||
map.async = function mapAsync(fn, iterable, callback){ | ||
var clone, iter, i, len, key, value, | ||
list = isArray(iterable); | ||
iter = list ? Array.prototype.slice.call(iterable) : Object.keys(iterable); | ||
iter = list ? slice.call(iterable) : objectKeys(iterable); | ||
clone = list ? iter : {}; | ||
(function next(i, error, rpl){ | ||
key = list ? i-1 : iter[i-1]; | ||
@@ -186,2 +211,62 @@ value = list ? clone[i] : iterable[ iter[i] ]; | ||
/* | ||
* Return a memoized version of 'fn' | ||
*/ | ||
function memoize(fn, hash){ | ||
var mem = {}; | ||
!hash && ( hash = function(n){ return n; }); | ||
return function memoizedFn(){ | ||
var key = hash.apply(undefined, arguments); | ||
return mem.hasOwnProperty(key) ? mem[key] : ( mem[ key ] = fn.apply(undefined, arguments) ); | ||
}; | ||
} | ||
memoize.async = function memoizeAsync(fn, hash){ | ||
var mem = {}, queues = {}; | ||
!hash && ( hash = function(n){ return n; }); | ||
return function memoizedAsyncFn(){ | ||
var args = slice.call(arguments), | ||
key = hash.apply(undefined, args), | ||
callback = args.pop(); | ||
if( key in mem ){ | ||
callback.apply(undefined, mem[ key ]); | ||
return; | ||
} | ||
if( key in queues){ | ||
queues[key].push(callback); | ||
return; | ||
} | ||
queues[key] = [callback]; | ||
args.push(function asyncMemoizeCallback(error/*, result */){ | ||
var args = arguments; | ||
!error && ( mem[ key ] = args ); | ||
(function next(i){ | ||
if( i >= queues[key].length ){ | ||
delete queues[key]; | ||
return; | ||
} | ||
var cb = queues[ key ][i]; | ||
setTimeout(function(){ | ||
cb.apply(undefined, args); | ||
}, 0); | ||
next(i+1); | ||
}(0)); | ||
}); | ||
fn.apply(undefined, args); | ||
}; | ||
}; | ||
/** | ||
@@ -193,3 +278,3 @@ * Return a new function which will call 'fn' with the positional arguments 'args' | ||
return function(){ | ||
var args = Array.prototype.slice.call(initialArgs,0); | ||
var args = slice.call(initialArgs,0); | ||
Array.prototype.push.apply(args,arguments); | ||
@@ -203,3 +288,3 @@ return fn.apply(ctx,args); | ||
*/ | ||
function reduce(fn,iterable){ | ||
function reduce(fn, iterable){ | ||
var accumulator = iterable[0]; | ||
@@ -214,5 +299,5 @@ | ||
reduce.async = function reduce_async(fn,iterable,callback){ | ||
reduce.async = function reduceAsync(fn, iterable,callback){ | ||
(function(i, error, accumulator){ | ||
if(error || i>=iterable.length){ | ||
@@ -229,10 +314,11 @@ callback(error, accumulator); | ||
return { | ||
"compose":compose, | ||
"curry":curry, | ||
"each":each, | ||
"filter":filter, | ||
'juxt':juxt, | ||
"map":map, | ||
"partial":partial, | ||
"reduce":reduce | ||
"compose": compose, | ||
"curry": curry, | ||
"each": each, | ||
"filter": filter, | ||
'juxt': juxt, | ||
"map": map, | ||
"memoize": memoize, | ||
"partial": partial, | ||
"reduce": reduce | ||
}; | ||
@@ -239,0 +325,0 @@ |
{ | ||
"name":"functools", | ||
"version":"1.2.0", | ||
"version":"1.3.0", | ||
"description":"A minimal library of functional operations", | ||
@@ -9,3 +9,10 @@ "author":"Azer Koculu <azer@kodfabrik.com>", | ||
"main":"./lib/functools", | ||
"repository": { "type":"git", "url" : "http://github.com/azer/functools.git" } | ||
"repository": { "type":"git", "url" : "http://github.com/azer/functools.git" }, | ||
"devDependencies":{ | ||
"lowkick":"0.x", | ||
"highkick":"1.x" | ||
}, | ||
"scripts":{ | ||
"test":"./node_modules/.bin/highkick test/main.js" | ||
} | ||
} |
244
README.md
@@ -60,3 +60,3 @@ functools is a JavaScript library for functional programming. | ||
or | ||
or | ||
@@ -67,4 +67,33 @@ ```bash | ||
# WHAT'S NEW? | ||
09.06.2012 | ||
* each.async | ||
* memoize | ||
* memoize.async | ||
06.06.2012 | ||
* IE6, IE7 and IE8 bugfixes | ||
* better testing | ||
# API | ||
* [compose](#compose) | ||
* [compose.async](#compose.async) | ||
* [curry](#curry) | ||
* [each](#each) | ||
* [each.async](#each.async) | ||
* [filter](#filter) | ||
* [filter.async](#filter.async) | ||
* [juxt](#juxt) | ||
* [juxt.async](#juxt.async) | ||
* [map](#map) | ||
* [map.async](#map.async) | ||
* [memoize](#memoize) | ||
* [memoize.async](#memoize.async) | ||
* [partial](#partial) | ||
* [reduce](#reduce) | ||
* [reduce.async](#reduce.async) | ||
<a name="compose" /> | ||
## compose(*functions ...*)(*value*) | ||
@@ -81,2 +110,3 @@ | ||
<a name="compose.async" /> | ||
## compose.async(*functions ...*)(*value*,*callback*) | ||
@@ -100,3 +130,3 @@ | ||
``` | ||
<a name="curry" /> | ||
## curry(*function*, *args ...*) | ||
@@ -115,32 +145,98 @@ | ||
<a name="each" /> | ||
## each(*function*,*iterable*) | ||
## partial(*function*,*initial arguments*,*context *) | ||
Call *function* once for each element in *iterable*. | ||
Return a new function which will call *function* with the gathered arguments. | ||
```javascript | ||
each(function(el,ind,list){ console.assert( el == list[ind] ); }, [3, 1, 4]); | ||
``` | ||
<a name="each.async" /> | ||
## each(*function*,*iterable*,*callback*) | ||
Call async *function* once for each element in *iterable*, and *callback* after iteration. | ||
```javascript | ||
function testPartial(){ | ||
var args = reduce(function(x,y){ x+", "+y },arguments); | ||
console.log("this:",this); | ||
console.log("args:",args); | ||
} | ||
> function uploadFile(filename, index, files, callback){ console.log('Uploading ', filename); callback(); } | ||
partial(testPartial, [3,14], 3.14159)(1,5,9); | ||
> each.async(uploadFile, [ '/docs/intro', '/docs/body', '/docs/outro' ], function(error){ | ||
if(error){ | ||
console.log('Failed to upload files'); | ||
throw error; | ||
} | ||
console.log('All files has been uploaded successfully'); | ||
}); | ||
> Uploading /docs/intro | ||
Uploading /docs/body | ||
Uploading /docs/outro | ||
All files has been uploaded successfully | ||
``` | ||
The example code above will output: | ||
<a name="filter" /> | ||
## filter(*function*,*iterable*) | ||
Construct a new array from those elements of *iterable* for which *function* returns true. | ||
```javascript | ||
filter(function(el,ind,list){ return el%2==0 }, [3, 1, 4]); // returns [4] | ||
``` | ||
this: 3.14159 | ||
args: 3,14,1,5,9 | ||
<a name="filter.async" /> | ||
## filter.async(*function*,*iterable*, *callback*) | ||
Call async *function* once for each element in *iterable*, receiving a boolean | ||
parameter, and construct a new array of all the values for which *function* | ||
produces *true* | ||
```javascript | ||
var users = [ 3, 5, 8, 13, 21 ]; // only user#3 and user#8 have permission in this example | ||
function hasPermission(userId, callback){ ... callback(/* true or false */); } | ||
filter.async(hasPermission, [3, 1, 4], function(permittedUsers){ | ||
assert.equal(permittedUsers.length, /* ? */); | ||
}); | ||
``` | ||
## each(*function*,*iterable*) | ||
<a name="juxt" /> | ||
## juxt(*functions ...*) | ||
Call *function* once for element in *iterable*. | ||
Take a set of functions, return a function that is the juxtaposition of those | ||
functions. The returned function takes a variable number of arguments and | ||
returns a list containing the result of applying each fn to the arguments. | ||
```javascript | ||
each(function(el,ind,list){ console.assert( el == list[ind] ); }, [3,1,4]); | ||
function inc1(n){ return n+1 }; | ||
function inc2(n){ return n+2 }; | ||
function inc3(n){ return n+3 }; | ||
juxt(inc1, inc2, inc3)(314); // returns [315,316,317] | ||
``` | ||
<a name="juxt.async" /> | ||
## juxt.async(*functions ...*) | ||
Async implementation of *juxt*. | ||
```javascript | ||
function turkish(word, callback){ /* some magic here */ } | ||
function french(word, callback){ /* some magic here */ } | ||
function polish(word, callback){ /* some magic here */ } | ||
juxt.async(turkish, french, polish)("hello", function(error, results){ | ||
assert.equal(results[0], "merhaba"); | ||
assert.equal(results[1], "bonjour"); | ||
assert.equal(results[2], "cześć"); | ||
}); | ||
``` | ||
<a name="map" /> | ||
## map(*function*,*iterable*) | ||
@@ -153,7 +249,7 @@ | ||
function square(n){ | ||
function square(n){ | ||
return n*n; | ||
} | ||
map(square,[3,1,4,1,5,9]); // returns [9,1,16,1,25,81] | ||
map(square, [3,1,4,1,5,9]); // returns [9,1,16,1,25,81] | ||
``` | ||
@@ -170,5 +266,6 @@ | ||
map(capitalize, dict); // returns { 'en':'Hello', 'tr':'Merhaba', 'fr':'Bonjour' } | ||
map(capitalize, capitalize); // returns { 'en':'Hello', 'tr':'Merhaba', 'fr':'Bonjour' } | ||
``` | ||
<a name="map.async" /> | ||
## map.async(*function*,*iterable*, *callback*) | ||
@@ -189,60 +286,72 @@ | ||
## filter(*function*,*iterable*) | ||
<a name="memoize" /> | ||
## memoize(*function*,*hasher*) | ||
Construct a new array from those elements of *iterable* for which *function* returns true. | ||
Return a memoized version of *function*. *hasher* is optional. | ||
```javascript | ||
filter(function(el,ind,list){ return el%2==0 },[3,1,4]); // returns [4] | ||
> function myfunc(n){ | ||
console.log("doing some work"); | ||
return n + 10; | ||
} | ||
> var myfuncMemo = memoize(myfunc); | ||
> myfuncMemo(1); | ||
"doing some work" | ||
11 | ||
> myfuncMemo(1); | ||
11 | ||
> myfuncMemo(20); | ||
"doing some work" | ||
30 | ||
> myfuncMemo(20); | ||
30 | ||
``` | ||
## filter.async(*function*,*iterable*, *callback*) | ||
<a name="memoize.async" /> | ||
## memoize.async(*function*, *hasher*) | ||
Call async *function* once for each element in *iterable*, receiving a boolean | ||
parameter, and construct a new array of all the values for which *function* | ||
produces *true* | ||
Memoize given async *function* if it doesn't produce any error. | ||
```javascript | ||
> function readFile(){ console.log('doing some work'); ... callback(undefined, buffer); } | ||
var users = [ 3, 5, 8, 13, 21 ]; // only user#3 and user#8 have permission in this example | ||
> var readFileMemo = memoize.async(readFile); | ||
function hasPermission(userId, callback){ ... callback(/* true or false */); } | ||
> readFileMemo('/docs/readme', function(error, content){ | ||
console.log(content); | ||
}); | ||
"doing some work" | ||
filter.async(hasPermission, users, function(permittedUsers){ | ||
assert.equal(permittedUsers.length, 4); | ||
> "This is the Readme file" | ||
> readFileMemo('/docs/readme', function(error, content){ | ||
console.log(content); | ||
}); | ||
"This is the Readme file" | ||
``` | ||
<a name="partial" /> | ||
## partial(*function*,*initial arguments*,*context *) | ||
Return a new function which will call *function* with the gathered arguments. | ||
## juxt(*functions ...*) | ||
```javascript | ||
function testPartial(){ | ||
var args = reduce(function(x,y){ x+", "+y },arguments); | ||
Take a set of functions, return a function that is the juxtaposition of those | ||
functions. The returned function takes a variable number of arguments and | ||
returns a list containing the result of applying each fn to the arguments. | ||
console.log("this:",this); | ||
console.log("args:",args); | ||
} | ||
```javascript | ||
function inc1(n){ return n+1 }; | ||
function inc2(n){ return n+2 }; | ||
function inc3(n){ return n+3 }; | ||
juxt(inc1, inc2, inc3)(314); // returns [315,316,317] | ||
partial(testPartial, [3,14], 3.14159)(1,5,9); | ||
``` | ||
## juxt.async(*functions ...*) | ||
Async implementation of *juxt*. | ||
```javascript | ||
function turkish(word, callback){ /* some magic here */ } | ||
function french(word, callback){ /* some magic here */ } | ||
function polish(word, callback){ /* some magic here */ } | ||
juxt.async(turkish, french, polish)("hello", function(error, results){ | ||
assert.equal(results[0], "merhaba"); | ||
assert.equal(results[1], "bonjour"); | ||
assert.equal(results[2], "cześć"); | ||
}); | ||
The example code above will output: | ||
``` | ||
this: 3.14159 | ||
args: 3,14,1,5,9 | ||
``` | ||
<a name="reduce /> | ||
## reduce(*function*,*iterable*) | ||
@@ -257,2 +366,3 @@ | ||
<a name="reduce.async" /> | ||
## reduce.async(*function*,*iterable*, *callback*) | ||
@@ -276,3 +386,27 @@ | ||
# Testing | ||
## On NodeJS: | ||
``` | ||
$ npm test | ||
``` | ||
## On Browsers | ||
Run `make test` command to publish the tests on `localhost:1314`. Visit the URL using the browser on which you want to run the tests. Stop the server (Ctrl+C) when you're done with testing. | ||
To see the summary of results; | ||
``` | ||
$ make test do=verify | ||
Not Tested: firefox, ie8, ie7 | ||
Passed: ie6, webkit | ||
Revision: 1.3.0 | ||
Results Source: test/results.json | ||
Config: test/config.json | ||
``` | ||
# SEE ALSO | ||
- [Functional Programming - Eloquent JavaScript](http://eloquentjavascript.net/chapter6.html) |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
403
17658
2
6
243
1