UNPKG

obop

Version:

MongoDB-style object operators makes array manipulation easy: where/order/update/view

165 lines (149 loc) 4.22 kB
/*! update.js */ /** * This updates an array of items, or generates an update compiled function from update-clause operators object. * Since version 0.0.4, dot notation (e.g. foo.bar.baz) is supported to handle child nodes. * * @method obop.prototype.update * @param {Array} [array] - source array to update * @param {Object|Function} update - update operators or function * @see http://docs.mongodb.org/manual/reference/operator/#update-operators * @returns {Array} Array of updated items (if source array given) * @returns {Function} Update function compiled for Array.prototype.map() method (if source array not given) * @returns {Error} Error instance (if source array not given but invalid update operaters given) * * @example * var list = [ * { name: "apple", price: 50 }, * { name: "orange", price: 10 }, * { name: "pineapple", price: 70 }, * { name: "grape", price: 30 } * ]; * * // update without obop * var out1 = list.filter(function(item) { * item.sale = true; * item.price -= 5; * return item; * }); * console.log(out1); * * // update with obop * var update = { $set: { sale: true }, $inc: { price: -5 } }; * var out2 = obop.update(list, update); * console.log(out2); * * // compile a update function * var update = { $set: { sale: true }, $inc: { price: -5 } }; * var updater = obop.update(update); * if (updater instanceof Error) throw updater; * var out3 = list.map(updater); * console.log(out3); */ exports.update = function(array, param) { var func; var len = arguments.length; if (len === 1) { func = update.call(this, array); if (func instanceof Error) throw func; return func; } else if (len === 2) { if (array instanceof Array) { func = update.call(this, param); if (func instanceof Error) throw func; return array.map(func); } else { throw new Error('Invalid argument type: ' + array); } } else { throw new Error('Invalid arguments length: ' + len); } }; function update(param) { var obop = this; var _nop = null; // no operation var err; // function type if ('function' === typeof param) { return param; } // default parameters param = param || {}; // no operators: through if (!Object.keys(param)) { return _nop; } // other types than object if ('object' !== typeof param) { return new Error('Invalid update operator type: ' + update); } // parse operators var array = []; Object.keys(param).forEach(function(op) { if (err) return; var gen = obop.$update[op]; if (!gen) { err = new Error('Unknown update operator: ' + op); return; } var hash = param[op]; var root = {}; var hasdot = {}; for (var key in hash) { if (/(^|\.)__proto__(\.|$)/.test(key)) { err = new Error('Invalid target: ' + key); return; } var val = hash[key]; var pos = key.indexOf('.'); if (pos > -1) { // $op: { "pre.post": "val" } var pre = key.substr(0, pos); var post = key.substr(pos + 1); hasdot[pre] = hasdot[pre] || {}; hasdot[pre][post] = val; } else { // $op: { "key": "val" } root[key] = val; } } // $op: { "key": "val" } if (Object.keys(root).length) { var f = gen(root); array.push(f); } // $op: { "pre.post": "val" } if (Object.keys(hasdot).length) { Object.keys(hasdot).forEach(function(key) { var tmp = {}; tmp[op] = hasdot[key]; var update = obop.update(tmp); var f = function(item) { var hash = item[key]; if ('object' !== typeof hash) { hash = item[key] = {}; } item[key] = update(hash); return item; }; array.push(f); }); } }); if (err) return err; // one operator type used if (array.length < 2) { return array.shift() || _nop; } // more operator types used return join(array); } function join(array) { var len = array.length; return function(item) { for (var i = 0; i < len; i++) { var update = array[i]; item = update(item); } return item; }; }