diff --git a/lib/include_utils.js b/lib/include_utils.js index 5d1dd1e5..8da849db 100644 --- a/lib/include_utils.js +++ b/lib/include_utils.js @@ -1,8 +1,9 @@ module.exports.buildOneToOneIdentityMap = buildOneToOneIdentityMap; module.exports.buildOneToManyIdentityMap = buildOneToManyIdentityMap; +module.exports.buildOneToOneIdentityMapWithOrigKeys = buildOneToOneIdentityMapWithOrigKeys; module.exports.join = join; /** - * Effectivly builds associative map on id -> object relation. + * Effectively builds associative map on id -> object relation. * Map returned in form of object with ids in keys and object as values. * @param objs array of objects to build from * @param idName name of property to be used as id. Such property considered to be unique across array. @@ -19,6 +20,58 @@ function buildOneToOneIdentityMap(objs, idName) { return idMap; } +/** + * Builds key -> value map on js object base. As js object keys can be only strings keys are stored on value side. + * So, each value should be an object like that: + * { origKey: 34, value: {...}} + * origKey field name should be passed as parameter to function. + * + * @param origKeyField filed name on value side to pick original key from. + * @returns empty object to be filled with key-value pair and additional methods `keys` and `originalKeys` + */ +function newIdMap(origKeyField) { + //var idMap = Object.create(null); // not any single properties within our identity map + var idMap = {}; + Object.defineProperty(idMap, "keys", { // can ask for keys simply by idMap.keys + get: function(){ return Object.keys(this); }, + enumerable: false // explicitly non-enumerable + }); + Object.defineProperty(idMap, "originalKeys", { // can ask for all original keys by idMap.originalKeys + get: function(){ + var keys = this.keys; + var origKeys = []; + for(var i = 0; i < keys.length; i++) { + var origKey = this[keys[i]][origKeyField]; + origKeys.push(origKey); + } + return origKeys; + }, + enumerable: false // explicitly non-enumerable + }); + return idMap; +} +/** + * Effectively builds associative map on id -> object relation and stores original keys. + * Map returned in form of object with ids in keys and object as values. + * @param objs array of objects to build from + * @param idName name of property to be used as id. Such property considered to be unique across array. + * In case of collisions last wins. For non-unique ids use buildOneToManyIdentityMap() + * @returns {{}} object where keys are ids and values are objects itself + */ +function buildOneToOneIdentityMapWithOrigKeys(objs, idName) { + var idMap = newIdMap("originalKey"); + + for(var i = 0; i < objs.length; i++) { + var obj = objs[i]; + var id = obj[idName]; + idMap[id.toString()] = { + originalKey: id, + value: obj + }; + } + return idMap; +} + /** * Effectively builds associate map on id -> Array[Object]. * Map returned in form of object with ids in keys and array of objects with given id. diff --git a/test/include_util.test.js b/test/include_util.test.js index bf3af4af..adbeffe2 100644 --- a/test/include_util.test.js +++ b/test/include_util.test.js @@ -32,6 +32,54 @@ describe('include_util', function(){ }); }); + describe('#buildOneToOneIdentityMapWithOrigKeys', function(){ + it('should return an object with keys', function(){ + var objs = [ + {id: 11, letter: "A"}, + {id: 22, letter: "B"} + ]; + var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, "id"); + result.should.be.an.instanceOf(Object); + result.should.have.property("11"); + result.should.have.property("22"); + Object.keys(result).should.have.lengthOf(2); // no additional properties + }); + it('should return all stringized keys with .keys method', function(){ + var objs = [ + {id: 11, letter: "A"}, + {id: 22, letter: "B"}, + {id: "cc", letter: "C"} + ]; + var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, "id"); + var keys = result.keys; + keys.should.be.instanceOf(Array); + keys.should.have.lengthOf(3); + keys.should.be.eql(['11', '22', 'cc']); + }); + it("should return all original keys with .originalKeys method", function(){ + var objs = [ + {id: 11, letter: "A"}, + {id: 22, letter: "B"}, + {id: "vv", letter: "V"} + ]; + var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, "id"); + var origKeys = result.originalKeys; + origKeys.should.be.instanceOf(Array); + origKeys.should.have.lengthOf(3); + origKeys.should.be.eql([11, 22, 'vv']); + }); + it('should have .keys and .originalKeys in same order', function(){ + var objs = [ + {id: 11, letter: "A"}, + {id: 22, letter: "B"}, + {id: "vv", letter: "V"} + ]; + var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, "id"); + var keys = result.keys; + var origKeys = result.originalKeys; + origKeys.map(function(a){return a.toString();}).should.be.eql(keys); + }); + }); describe('#buildOneToManyIdentityMap', function(){ it('should return an object with keys', function(){ var objs = [