Permalink
Browse files

Adds .each and makes .map also collect result.

  • Loading branch information...
1 parent e1773e8 commit 2cf70c1223af37db0ce003e498131ee53b68a8e2 Chakrit Wichian committed Aug 18, 2013
Showing with 108 additions and 52 deletions.
  1. +39 −6 lib/jam.js
  2. +69 −46 test/helpers.js
View
@@ -131,22 +131,22 @@ module.exports = (function() {
}
};
- // ## jam.map( array, iterator( next, element, index ) )
-
- // Execute the given `iterator` for each element given in the `array`. The iterator is
- // given a `next` function and the element to act on.
+ // ## jam.each( array, iterator( next, element, index ) )
+
+ // Execute the given `iterator` function for each element given in the `array`. The
+ // iterator is given a `next` function and the element to act on.
//
// You can also pass `arguments` and `"strings"` as an array.
//
// Under the hood, a JAM step is added for each element. So the iterator will be
// called serially, one after another finish. A parallel version maybe added in the
// future.
- func.map = function(array, iterator) {
+ func.each = function(array, iterator) {
ensureArray(array, 'array');
ensureFunc(iterator, 'iterator');
return function(next) {
- // Builds another JAM chain internally.
+ // Builds another JAM chain internally
var chain = jam(jam.identity)
, count = array.length;
@@ -158,6 +158,39 @@ module.exports = (function() {
};
};
+ // ## jam.map( array, iterator( next, element, index ) )
+
+ // Works exactly like the `each` helper but if a value is passed to the iterator's
+ // `next` function, it is collected into a new array which will be passed to the next
+ // function in the JAM chain after `map`.
+ func.map = function(array, iterator) {
+ ensureArray(array, 'array');
+ ensureFunc(iterator, 'iterator');
+
+ return function(next) {
+ // Builds another JAM chain internally and collect results.
+ // TODO: Dry with .each?
+ var chain = jam(jam.identity)
+ , count = array.length
+ , result = [];
+
+ for (var i = 0; i < count; i++) (function(element, i) {
+ chain = chain(function(next, previous) {
+ result.push(previous);
+ iterator(next, element, i);
+ });
+ })(array[i], i);
+
+ chain = chain(function(next, last) {
+ result.push(last);
+ result.shift(); // discard first undefined element
+ next(null, result);
+ });
+
+ return chain(next);
+ };
+ };
+
// ## jam.timeout( timeout )
// Pauses the chain for the specified `timeout` using `setTimeout`. Useful for
View
@@ -9,6 +9,8 @@
, NON_ARRAYS = [undefined, null, 123, 'string', { }, function() { }]
, NON_NUMS = [undefined, null, 'string', { }, function() { }, []];
+ var ITERABLES = ['each', 'map'];
+
// functions with multiple usage modes
describeModes = function(modes, tests) {
@@ -141,52 +143,6 @@
});
}); // .call
- describe('.map function', function() {
- it('should be exported', function() {
- assert.typeOf(this.jam.map, 'function');
- });
-
- it('should throws if array argument missing or not an array', function() {
- var me = this;
- NON_ARRAYS.forEach(function(thing) {
- assert.throws(function() { me.jam.map(thing); }, /array/i);
- });
- });
-
- it('should throws if iterator argument missing or not a function', function() {
- var me = this;
- NON_FUNCTIONS.forEach(function(thing) {
- assert.throws(function() { me.jam.map([1,2,3], thing); }, /iterator/i);
- });
- });
-
- it('should calls iterator for each element in the given array serially', function(done) {
- var elements = ['one', 'two', 'three']
- , index = 0;
-
- this.jam(this.jam.map(elements, function(next, element, index_) {
- assert.equal(index_, index);
- assert.equal(element, elements[index]);
- index++;
- next();
- }))(done);
- });
-
- it('should forwards error properly when an iterator calls `next()` with an Error`', function(done) {
- var err = new Error('test error')
- , elements = ['one', 'two', 'three']
- , index = 0;
-
- this.jam(this.jam.map(elements, function(next, element, index_) {
- next((index_ < 2) ? undefined : err); // throw on last element
-
- }))(function(e) {
- assert.equal(e, err); // should be forwarded here
- done();
- });
- });
- }); // .map
-
describe('.timeout function', function() {
it('should be exported', function() {
assert.typeOf(this.jam.timeout, 'function');
@@ -214,6 +170,73 @@
});
});
+ describe('iterable functions', function() {
+ ITERABLES.forEach(function(func) {
+
+ describe('.' + func + ' function', function() {
+ it('should be exproted', function() {
+ assert.typeOf(this.jam[func], 'function');
+ });
+
+ it('should throws if array argument missing or not an array', function() {
+ var me = this;
+ NON_ARRAYS.forEach(function(thing) {
+ assert.throws(function() { me.jam[func](thing); }, /array/i);
+ });
+ });
+ });
+
+ it('should throws if iterator argument missing or not a function', function() {
+ var me = this;
+ NON_FUNCTIONS.forEach(function(thing) {
+ assert.throws(function() { me.jam[func]([1,2,3], thing); }, /iterator/i);
+ });
+ });
+
+ it('should calls iterator for each element in the given array serially', function(done) {
+ var elements = ['one', 'two', 'three']
+ , index = 0;
+
+ this.jam(this.jam[func](elements, function(next, element, index_) {
+ assert.equal(index_, index);
+ assert.equal(element, elements[index]);
+ index++;
+ next();
+ }))(done);
+ });
+
+ it('should forwards error properly when an iterator calls `next()` with an Error`', function(done) {
+ var err = new Error('test error')
+ , elements = ['one', 'two', 'three']
+ , index = 0;
+
+ this.jam(this.jam[func](elements, function(next, element, index_) {
+ next((index_ < 2) ? undefined : err); // throw on last element
+
+ }))(function(e) {
+ assert.equal(e, err); // should be forwarded here
+ done();
+ });
+ });
+
+ });
+ }); // iterables
+
+ describe('.map function', function() {
+ it('should also returns collected results passed to next()', function(done) {
+ var elements = [1, 2, 3];
+
+ this.jam(this.jam.map(elements, function(next, element, index) {
+ return next(null, element * 2);
+ }))(function(next, result) {
+ for (var i = 0; i < elements.length; i++) {
+ assert.equal(elements[i] * 2, result[i]);
+ }
+ next()
+ })(done);
+ });
+ });
+
});
})();

0 comments on commit 2cf70c1

Please sign in to comment.