├── .gitignore ├── LICENCE ├── README.md ├── index.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | *.err 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Raynos. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # map-async 2 | 3 | Asynchronously map over a list 4 | 5 | ## Example 6 | 7 | ``` js 8 | var map = require("map-async") 9 | 10 | map({ 11 | foo: "bar" 12 | , baz: "boz" 13 | }, function iterator(value, key, cb) { 14 | cb(null, value + value) 15 | }, function finish(err, result) { 16 | /* ... */ 17 | }) 18 | ``` 19 | 20 | ## Installation 21 | 22 | `npm install map-async` 23 | 24 | ## Contributors 25 | 26 | - Raynos 27 | 28 | ## MIT Licenced 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = map 2 | 3 | function map(list, iterator, context, callback) { 4 | var keys = Object.keys(list) 5 | , returnValue = Array.isArray(list) ? [] : {} 6 | , count = keys.length 7 | 8 | if (typeof context === "function") { 9 | callback = context 10 | context = this 11 | } 12 | 13 | if (keys.length === 0) { 14 | return callback(null, returnValue) 15 | } 16 | 17 | for (var i = 0, len = keys.length; i < len; i++) { 18 | var key = keys[i] 19 | , value = list[key] 20 | 21 | invokeIterator(iterator, 22 | next(key), context, value, key, list) 23 | } 24 | 25 | function next(key) { 26 | return handler 27 | 28 | function handler(err, newValue) { 29 | if (err) { 30 | return callback && callback(err) 31 | } 32 | 33 | returnValue[key] = newValue 34 | 35 | if (--count === 0) { 36 | callback && callback(null, returnValue) 37 | } 38 | } 39 | } 40 | } 41 | 42 | function invokeIterator(iterator, done, self, value, key, list) { 43 | var length = iterator.length 44 | 45 | if (length === 1) { 46 | iterator.call(self, done) 47 | } else if (length === 2) { 48 | iterator.call(self, value, done) 49 | } else if (length === 3) { 50 | iterator.call(self, value, key, done) 51 | } else { 52 | iterator.call(self, value, key, list, done) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "map-async", 3 | "version": "0.1.1", 4 | "description": "Asynchronously map over a list", 5 | "keywords": [], 6 | "author": "Raynos ", 7 | "repository": "git://github.com/Raynos/map-async.git", 8 | "main": "index", 9 | "homepage": "https://github.com/Raynos/map-async", 10 | "contributors": [ 11 | { 12 | "name": "Jake Verbaten" 13 | } 14 | ], 15 | "bugs": { 16 | "url": "https://github.com/Raynos/map-async/issues", 17 | "email": "raynos2@gmail.com" 18 | }, 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "tap": "~0.3.1", 22 | "sinon": "~1.4.2" 23 | }, 24 | "license": "MIT", 25 | "scripts": { 26 | "test": "tap --stderr --tap ./test" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var test = require("tap").test 2 | , sinon = require("sinon") 3 | , map = require("..") 4 | 5 | test("filter calls each iterator", function (t) { 6 | var item = createItem() 7 | , spy = sinon.spy() 8 | , iterator = function (value, callback) { 9 | spy.apply(this, arguments) 10 | callback(null, value + value) 11 | } 12 | , done = sinon.spy() 13 | 14 | map(item, iterator, done) 15 | 16 | t.ok(spy.calledThrice, "iterator is not called three times") 17 | t.ok(spy.getCall(0).calledWith("a1", sinon.match.func), 18 | "iterator called with wrong arguments") 19 | t.ok(spy.getCall(1).calledWith("b1", sinon.match.func), 20 | "iterator called with wrong arguments") 21 | t.ok(spy.getCall(2).calledWith("c1", sinon.match.func), 22 | "iterator called with wrong argument") 23 | t.ok(done.calledOnce, "done was not called") 24 | t.deepEqual(done.args[0], [null, { 25 | a: "a1a1" 26 | , b: "b1b1" 27 | , c: "c1c1" 28 | }], "done not called correctly") 29 | 30 | t.end() 31 | }) 32 | 33 | test("map calls iterator with correct this value", function (t) { 34 | var item = createItem() 35 | , iterator = sinon.spy() 36 | , thisValue = {} 37 | , done = sinon.spy() 38 | 39 | map(item, iterator, thisValue, done) 40 | 41 | t.ok(iterator.calledOn(thisValue), "this value is incorrect") 42 | 43 | t.end() 44 | }) 45 | 46 | test("calls iterator with the callback last", function (t) { 47 | var item = createItem() 48 | , spy = sinon.spy() 49 | , iterator = function (value, index, list, callback) { 50 | spy.apply(this, arguments) 51 | callback() 52 | } 53 | , done = sinon.spy() 54 | 55 | map(item, iterator, done) 56 | 57 | t.ok(spy.calledThrice, "iterator was not called thrice") 58 | 59 | t.end() 60 | }) 61 | 62 | test("filter returns an array when called on array", function (t) { 63 | var array = [1, 2, 3] 64 | , spy = sinon.spy() 65 | , iterator = function (v, callback) { 66 | spy.apply(this, arguments) 67 | callback(null, v * v) 68 | } 69 | , done = sinon.spy() 70 | 71 | 72 | map(array, iterator, done) 73 | 74 | t.ok(done.calledOnce, "done was not called") 75 | t.ok(Array.isArray(done.args[0]), "result is not an array") 76 | t.deepEqual(done.args[0], [null, [1, 4, 9]], "result is incorrect") 77 | 78 | t.end() 79 | }) 80 | 81 | test("returns error appropiately", function (t) { 82 | var item = createItem() 83 | , errorValue = {} 84 | , iterator = function(callback) { 85 | callback(errorValue) 86 | } 87 | , done = sinon.spy() 88 | 89 | map(item, iterator, done) 90 | 91 | t.equal(done.args[0][0], errorValue, "this value is incorrect") 92 | 93 | t.end() 94 | }) 95 | 96 | function createItem() { 97 | return { 98 | a: "a1" 99 | , b: "b1" 100 | , c: "c1" 101 | } 102 | } 103 | --------------------------------------------------------------------------------