From 3077359a8206d7603728c43fa1668167ef7c89a3 Mon Sep 17 00:00:00 2001 From: Jesus Seijas Date: Tue, 28 Aug 2018 00:42:21 +0200 Subject: [PATCH 1/3] build: Updated dependencies due to major and critical audit issues. --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 37360f9..cf1b474 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,13 @@ }, "homepage": "https://github.com/ashraful-islam/async-array-loop#readme", "devDependencies": { + "benchmark": "^2.1.4", "chai": "^4.1.2", "coveralls": "^3.0.0", - "mocha": "^3.5.3", + "mocha": "^5.2.0", "mocha-lcov-reporter": "^1.3.0", "nyc": "^11.2.1", - "xo": "^0.18.2" + "xo": "^0.22.0" }, "xo": { "space": 2 From 38c569db013f00957873b6c5147e5938cf715b95 Mon Sep 17 00:00:00 2001 From: Jesus Seijas Date: Tue, 28 Aug 2018 00:51:51 +0200 Subject: [PATCH 2/3] refactor: refactorized functions to increase maintainability and readability. - Created iterate and iterate-promise functions as helpers for the others. The iterate is a foreach that also receives a function of "what to do when a value is getted" and "what to do when returning result". That way a filter creates a result array, when the value (boolean) is true then insert current element to result, and at the end return the array. The same for map, but when got a value (any) push it directly to result. Increases the maintainability due to reduce 3 different methods (foreach, map and filter) to a single point of maintenance (iterate). - Simplified the promises to use the callback version, so increases maintainability because is one single point of modification for promise and callback version. - Changed foreach promise test to check result values. - Added foreach test to check result values after timeout to check the "no callback provided" case. - Simplified the logic of the iteration to don't have a previous function call, so the logic is executed always inside next function. --- filter-promise.js | 52 +++++++++--------------------------- filter.js | 51 +++++++++-------------------------- foreach-promise.js | 40 +++++---------------------- foreach.js | 38 +++----------------------- iterate-promise.js | 9 +++++++ iterate.js | 39 +++++++++++++++++++++++++++ map-promise.js | 48 +++++++++------------------------ map.js | 48 +++++++++------------------------ reduce-promise.js | 43 ++++++----------------------- test/foreach-promise.test.js | 8 +++--- test/foreach.test.js | 9 +++++++ 11 files changed, 130 insertions(+), 255 deletions(-) create mode 100644 iterate-promise.js create mode 100644 iterate.js diff --git a/filter-promise.js b/filter-promise.js index c16d383..3f092fc 100644 --- a/filter-promise.js +++ b/filter-promise.js @@ -1,3 +1,5 @@ +const iterate = require('./iterate-promise'); + /** * Array filter to remove any falsy values from array * @param {array} to be operated on @@ -5,45 +7,17 @@ * @param {function} callback function, called when operations are completed */ function arrFilter(arr, func) { - return new Promise((resolve, reject) => { - if (!Array.isArray(arr)) { - return reject(new Error('First argument must be a valid array.')); - } - - if (typeof func !== 'function') { - return reject(new Error('Second argument must be a valid function.')); - } - - // Initialize - const len = arr.length; - let i = 0; - let _arr = []; - - if (len > 0) { - func(arr[i], i, arr, next); - } else { - return resolve(_arr); - } - - function next(err, isTrue) { - setImmediate(() => { - if (err) { - return reject(err); - } - - if (isTrue) { - _arr = _arr.concat([arr[i]]); - } - - if (++i < len) { - func(arr[i], i, arr, next); - return; - } - - resolve(_arr); - }); - } - }); + const result = []; + return iterate( + arr, + func, + (value, item) => { + if (value) { + result.push(item); + } + }, + () => result + ); } module.exports = arrFilter; diff --git a/filter.js b/filter.js index 8bcc355..35c7c9c 100644 --- a/filter.js +++ b/filter.js @@ -1,3 +1,5 @@ +const iterate = require('./iterate'); + /** * Array filter to remove any falsy values from array * @param {array} to be operated on @@ -5,45 +7,18 @@ * @param {function} callback function, called when operations are completed */ function arrFilter(arr, func, cb) { - const onComplete = (typeof cb === 'function') ? cb : function () { }; - - if (!Array.isArray(arr)) { - return onComplete(new Error('First argument must be a valid array.')); - } - - if (typeof func !== 'function') { - return onComplete(new Error('Second argument must be a valid function.')); - } - - // Initialize - let i = 0; - let _arr = []; - const len = arr.length; - - if (len > 0) { - func(arr[i], i, arr, next); - } else { - return onComplete(null, _arr); - } - - function next(err, isTrue) { - setImmediate(() => { - if (err) { - return onComplete(err); + const result = []; + return iterate( + arr, + func, + cb, + (value, item) => { + if (value) { + result.push(item); } - - if (isTrue) { - _arr = _arr.concat([arr[i]]); - } - - if (++i < len) { - func(arr[i], i, arr, next); - return; - } - - onComplete(null, _arr); - }); - } + }, + () => result + ); } module.exports = arrFilter; diff --git a/foreach-promise.js b/foreach-promise.js index ab36e1f..97a178e 100644 --- a/foreach-promise.js +++ b/foreach-promise.js @@ -1,41 +1,13 @@ +const iterate = require('./iterate-promise'); + /** * An array foreach operation * @param {array} to iterate over * @param {function} to receive each element * @returns {promise} */ -module.exports = function (arr, func) { - return new Promise((resolve, reject) => { - if (!Array.isArray(arr)) { - return reject(new Error('First argument must be a valid array.')); - } - - if (typeof func !== 'function') { - return reject(new Error('Second argument must be a valid function.')); - } - - // Initialize - let i = 0; - const len = arr.length; - - if (len > 0) { - func(arr[i], i, arr, next); - } else { - resolve(); - } - - function next(err) { - setImmediate(() => { - if (err) { - return reject(err); - } - - if (++i < len) { - return func(arr[i], i, arr, next); - } +function arrForeach(arr, func) { + return iterate(arr, func); +} - return resolve(); - }); - } - }); -}; +module.exports = arrForeach; diff --git a/foreach.js b/foreach.js index a060f56..c778508 100644 --- a/foreach.js +++ b/foreach.js @@ -1,3 +1,5 @@ +const iterate = require('./iterate'); + /** * An array foreach operation * @param {array} to iterate over @@ -5,41 +7,7 @@ * @param {function} callback called when iteration completes */ function arrForeach(arr, func, cb) { - const onComplete = (typeof cb === 'function') ? cb : function () { }; - - if (!Array.isArray(arr)) { - return onComplete(new Error('First argument must be a valid array.')); - } - - if (typeof func !== 'function') { - return onComplete(new Error('Second argument must be a valid function.')); - } - - // Initialize - let i = 0; - const len = arr.length; - const _arr = arr.slice(); - - if (len > 0) { - func(_arr[i], i, _arr, next); - } else { - return onComplete(); - } - - function next(err) { - setImmediate(() => { - if (err) { - return onComplete(err); - } - - if (++i < len) { - func(_arr[i], i, _arr, next); - return; - } - - return onComplete(); - }); - } + return iterate(arr, func, cb); } module.exports = arrForeach; diff --git a/iterate-promise.js b/iterate-promise.js new file mode 100644 index 0000000..ba2d605 --- /dev/null +++ b/iterate-promise.js @@ -0,0 +1,9 @@ +const iterate = require('./iterate'); + +function iteratePromise(arr, func, valueFn, resultFn) { + return new Promise((resolve, reject) => { + iterate(arr, func, (err, data) => err ? reject(err) : resolve(data), valueFn, resultFn); + }); +} + +module.exports = iteratePromise; diff --git a/iterate.js b/iterate.js new file mode 100644 index 0000000..3cb5e3a --- /dev/null +++ b/iterate.js @@ -0,0 +1,39 @@ +function iterate(arr, func, cb, valueFn, resultFn) { + const onComplete = typeof cb === 'function' ? cb : () => {}; + + if (!Array.isArray(arr)) { + return onComplete(new Error('First argument must be a valid array.')); + } + + if (typeof func !== 'function') { + return onComplete(new Error('Second argument must be a valid function.')); + } + + let i = 0; + const len = arr.length; + + function next() { + setImmediate(() => { + if (i >= len) { + if (resultFn) { + return onComplete(undefined, resultFn()); + } + return onComplete(); + } + func(arr[i], i, arr, (err, data) => { + if (valueFn) { + valueFn(data, arr[i], i, arr); + } + if (err) { + return onComplete(err); + } + i++; + return next(); + }); + }); + } + + next(); +} + +module.exports = iterate; diff --git a/map-promise.js b/map-promise.js index ccdb657..6b24236 100644 --- a/map-promise.js +++ b/map-promise.js @@ -1,3 +1,5 @@ +const iterate = require('./iterate-promise'); + /** * Map operation for array * @param {array} to iterated over @@ -5,41 +7,17 @@ * @returns {promise} */ function arrMap(arr, func) { - return new Promise((resolve, reject) => { - if (!Array.isArray(arr)) { - return reject(new Error('First argument must be a valid array.')); - } - - if (typeof func !== 'function') { - return reject(new Error('Second argument must be a valid function.')); - } - - // Initialize - let i = 0; - const len = arr.length; - const _arr = new Array(len); - - if (len > 0) { - func(arr[i], i, arr, next); - } else { - resolve(_arr); - } - - function next(err, newValue) { - setImmediate(() => { - if (err) { - return reject(err); - } - - if (i < len) { - _arr[i++] = newValue; - return func(arr[i], i, arr, next); - } - - return resolve(_arr); - }); - } - }); + const result = []; + return iterate( + arr, + func, + (value) => { + if (value) { + result.push(value); + } + }, + () => result + ); } module.exports = arrMap; diff --git a/map.js b/map.js index 4aecb95..a39ef43 100644 --- a/map.js +++ b/map.js @@ -1,3 +1,5 @@ +const iterate = require('./iterate'); + /** * Map operation for array * @param {array} to iterate over @@ -5,42 +7,18 @@ * @param {function} callback to indicate loop completion */ function arrMap(arr, func, cb) { - const onComplete = (typeof cb === 'function') ? cb : function () { }; - - if (!Array.isArray(arr)) { - return onComplete(new Error('First argument must be a valid array.')); - } - - if (typeof func !== 'function') { - return onComplete(new Error('Second argument must be a valid function.')); - } - - // Initialize - let i = 0; - const len = arr.length; - const _arr = new Array(len); - - if (len > 0) { - func(arr[i], i, arr, next); - } else { - onComplete(null, _arr); - } - - function next(err, newValue) { - setImmediate(() => { - if (err) { - return onComplete(err); - } - - if (i < len) { - _arr[i++] = newValue; - func(arr[i], i, arr, next); - return; + const result = []; + return iterate( + arr, + func, + cb, + (value) => { + if (value) { + result.push(value); } - - onComplete(null, _arr); - }); - } + }, + () => result + ); } module.exports = arrMap; diff --git a/reduce-promise.js b/reduce-promise.js index 3d8d79e..4e145af 100644 --- a/reduce-promise.js +++ b/reduce-promise.js @@ -1,3 +1,5 @@ +const arrReduce = require('./reduce'); + /** * Reduce method for array * @param {array} to iterate over @@ -5,43 +7,14 @@ * @param {any} initial value (optional) * @returns {promise} */ -function arrReduce(arr, func, accumulator) { +function arrReducePromise(arr, func, accumulator) { return new Promise((resolve, reject) => { - if (!Array.isArray(arr)) { - return reject(new Error('A valid array is required')); - } - - if (typeof func !== 'function') { - return reject(new Error('A valid iteration function is required')); - } - - // Initialize - const len = arr.length; - let i = 0; - - // Handle missing accumulator - const _accum = (arguments.length < 3) ? arr[i++] : accumulator; - - if (len > 0) { - func(_accum, arr[i], next); - } else { - resolve(); - } - - function next(err, prevValue) { - setImmediate(() => { - if (err) { - return reject(err); - } - - if (++i < len) { - return func(prevValue, arr[i], next); - } - - return resolve(prevValue); - }); + const args = [arr, func, (err, data) => err ? reject(err) : resolve(data)]; + if (arguments.length >= 3) { + args.push(accumulator); } + arrReduce(...args); }); } -module.exports = arrReduce; +module.exports = arrReducePromise; diff --git a/test/foreach-promise.test.js b/test/foreach-promise.test.js index 4d4c51e..a241993 100644 --- a/test/foreach-promise.test.js +++ b/test/foreach-promise.test.js @@ -9,17 +9,17 @@ const sampleArrCopy = [2,3,4,5,6,7,8,9,10,11,12]; describe('foreach:promise', function() { it('should resolve without error', function(done) { - + const result = []; arrForeach( sampleArr, function (el, idx, arr, next) { - + result.push(el); // some iteration should take place here next(null); } - ).then(function(result) { - expect(result).to.be.undefined; + ).then(function() { + expect(result).to.deep.equal(sampleArr); done(); }).catch(function(error) { done(err); diff --git a/test/foreach.test.js b/test/foreach.test.js index 08279df..97761b1 100644 --- a/test/foreach.test.js +++ b/test/foreach.test.js @@ -67,6 +67,15 @@ describe('foreach', function() { }); }); + it('should iterate all the elemnts even if no callback is provided', function(done) { + const result = []; + arrForeach(sampleArr,(el, i, arr, next) => { result.push(el); next(); }); + setTimeout(() => { + expect(result).to.deep.equal(sampleArr); + done(); + }, 10); + }); + it('should keep the original array intact', function() { expect(sampleArr).to.deep.equal(sampleArrCopy); }) From 8e45df3b81b58553937d1dbca95d3398f21ca124 Mon Sep 17 00:00:00 2001 From: Jesus Seijas Date: Tue, 28 Aug 2018 01:07:33 +0200 Subject: [PATCH 3/3] build: Make houndci happy --- .jshintrc | 3 +++ test/filter-promise.test.js | 2 ++ test/filter.test.js | 2 ++ test/foreach-promise.test.js | 2 ++ test/foreach.test.js | 2 ++ test/map-promise.test.js | 2 ++ test/map.test.js | 2 ++ test/reduce-promise.test.js | 2 ++ test/reduce.test.js | 2 ++ 9 files changed, 19 insertions(+) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..53b202c --- /dev/null +++ b/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 6 +} \ No newline at end of file diff --git a/test/filter-promise.test.js b/test/filter-promise.test.js index 377ad89..0ab6059 100644 --- a/test/filter-promise.test.js +++ b/test/filter-promise.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it */ + const arrFilter = require('./../filter-promise'); const { expect } = require('chai'); diff --git a/test/filter.test.js b/test/filter.test.js index 45de4f5..e311c9c 100644 --- a/test/filter.test.js +++ b/test/filter.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it */ + const arrFilter = require('./../filter'); const { expect } = require('chai'); diff --git a/test/foreach-promise.test.js b/test/foreach-promise.test.js index a241993..8a5236d 100644 --- a/test/foreach-promise.test.js +++ b/test/foreach-promise.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it */ + const arrForeach = require('./../foreach-promise'); const { expect } = require('chai'); diff --git a/test/foreach.test.js b/test/foreach.test.js index 97761b1..a84b657 100644 --- a/test/foreach.test.js +++ b/test/foreach.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it, setTimeout */ + const arrForeach = require('./../foreach'); const { expect } = require('chai'); diff --git a/test/map-promise.test.js b/test/map-promise.test.js index c4875b1..10f47eb 100644 --- a/test/map-promise.test.js +++ b/test/map-promise.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it */ + const arrMap = require('./../map-promise'); const { expect } = require('chai'); diff --git a/test/map.test.js b/test/map.test.js index 139e07c..dc21e87 100644 --- a/test/map.test.js +++ b/test/map.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it */ + const arrMap = require('./../map'); const { expect } = require('chai'); diff --git a/test/reduce-promise.test.js b/test/reduce-promise.test.js index cd65142..641538a 100644 --- a/test/reduce-promise.test.js +++ b/test/reduce-promise.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it */ + const arrReduce = require('./../reduce-promise'); const { expect } = require('chai'); diff --git a/test/reduce.test.js b/test/reduce.test.js index fd09a18..f436d08 100644 --- a/test/reduce.test.js +++ b/test/reduce.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* global it */ + const arrReduce = require('./../reduce'); const { expect } = require('chai');