From b8c389449aa7d4b1f9058091b42a3d3e95261f3c Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Thu, 29 Dec 2016 09:27:07 -0500 Subject: [PATCH] Added toggle and merge operations --- README.md | 33 ++++++ index.js | 40 +++++++ test/dot-prop-immutable-merge.spec.js | 138 +++++++++++++++++++++++++ test/dot-prop-immutable-toggle.spec.js | 33 ++++++ 4 files changed, 244 insertions(+) create mode 100644 test/dot-prop-immutable-merge.spec.js create mode 100644 test/dot-prop-immutable-toggle.spec.js diff --git a/README.md b/README.md index 4fd72ba..278f3ce 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,39 @@ dotProp.delete(obj, 'foo.0.bar'); //=> {foo: [{}, 'white-unicorn', 'silver-unicorn']} ``` +### toggle + +Delete a value by a dot path. + +```javascript +var obj = {foo: { bar: true } }; + +// toggle +dotProp.toggle(obj, 'foo.bar'); +//=> {foo: { bar: false } } +``` +### merge + +Merge a value by a dot path. +> The target value must be an object, array, null, or undefined. + + * If target is an object, Object.assign({}, target, param) is used. + * If target an array, target.concat(param) is used. + * If target is null or undefined, the value is simply set. + +```javascript +var obj = {foo: { bar: {a:1, b:2 } }; + +// merge object +dotProp.merge(obj, 'foo.bar', {c:3} ); +//=> {foo: { bar:{ a:1, b:2, c:3} } } + +var arr = {foo: { bar: [1, 2] } }; + +// merge array +dotProp.merge(arr, 'foo.bar', [3, 4] ); +//=> {foo: { bar:[1, 2, 3, 4 ] } +``` ## License [MIT](http://opensource.org/licenses/MIT) diff --git a/index.js b/index.js index 5e255a8..509cdba 100644 --- a/index.js +++ b/index.js @@ -79,6 +79,46 @@ module.exports.delete = function(obj, prop) { return deletePropImmutableRec(obj, prop, 0); }; +/** + * Toggles a value. The target value is evaluated using Boolean(currentValue). The result will always be a JSON boolean. + * Be careful with strings as target value, as "true" and "false" will toggle to false, but "0" will toggle to true. + * Here is what Javascript considers false: 0, -0, null, false, NaN, undefined, and the empty string ("") + * @param obj The object to evaluate. + * @param prop The path to the value. + */ +module.exports.toggle = function(obj, prop) { + var curVal = this.get(obj, prop); + return this.set(obj, prop, !Boolean(curVal)); +}; + +/** + * Merges a value. The target value must be an object, array, null, or undefined. + * If target is an object, Object.assign({}, target, param) is used. + * If target an array, target.concat(param) is used. + * If target is null or undefined, the value is simply set. + * @param obj The object to evaluate. + * @param prop The path to the value. + */ +module.exports.merge = function(obj, prop, val) { + var curVal = this.get(obj, prop); + if (typeof curVal === 'object') { + if (Array.isArray(curVal)){ + return this.set(obj, prop, curVal.concat(val)); + } else if (curVal === null){ + return this.set(obj, prop, val); + } + else { + var merged = Object.assign({}, curVal, val); + return this.set(obj, prop, merged); + } + } else if (typeof curVal === 'undefined'){ + return this.set(obj, prop, val); + } + else { + return obj; + } +}; + function getArrayIndex(head, obj) { if (head === '$end') { head = obj.length - 1; diff --git a/test/dot-prop-immutable-merge.spec.js b/test/dot-prop-immutable-merge.spec.js new file mode 100644 index 0000000..186eadf --- /dev/null +++ b/test/dot-prop-immutable-merge.spec.js @@ -0,0 +1,138 @@ +var dotProp = require('..'); + +describe('dot-prop-immutable.merge.spec.js', function () { + + var obj = { + a: 1, + b: { + x: 1, + y: 2 + }, + c: [1, 2], + d: null, + 'b.x': 10 + }; + + var arr = [1, {a: [1, 2]}]; + + var result; + describe('when have an object', () => { + + describe('merge an object value into object', () => { + + before(function () { + result = dotProp.merge(obj, 'b', {z: 3}); + }); + + it('should merge prop', () => { + expect(result).to.eql({ + a: 1, + b: { + x: 1, + y: 2, + z: 3 + }, + c: [1, 2], + d: null, + 'b.x': 10 + }); + }); + + it('invariant', objInvariant); + + }); + + describe('merge an array value into array', () => { + + before(function () { + result = dotProp.merge(obj, 'c', [3, 4]); + }); + + it('should merge prop', () => { + expect(result).to.eql({ + a: 1, + b: { + x: 1, + y: 2 + }, + c: [1, 2, 3, 4], + d: null, + 'b.x': 10 + }); + }); + + it('invariant', objInvariant); + + }); + + describe('merge an object value into null', () => { + + before(function () { + result = dotProp.merge(obj, 'd', {foo: 'bar'}); + }); + + it('should merge prop', () => { + expect(result).to.eql({ + a: 1, + b: { + x: 1, + y: 2 + }, + c: [1, 2], + d: {foo: 'bar'}, + 'b.x': 10 + }); + }); + + it('invariant', objInvariant); + + }); + + describe('merge an object value into undefined', () => { + + before(function () { + result = dotProp.merge(obj, 'z', {foo: 'bar'}); + }); + + it('should merge prop', () => { + expect(result).to.eql({ + a: 1, + b: { + x: 1, + y: 2 + }, + c: [1, 2], + d: null, + z: {foo: 'bar'}, + 'b.x': 10 + }); + }); + + it('invariant', objInvariant); + + }); + + + }); + + function objInvariant() { + expect(obj).to.eql({ + a: 1, + b: { + x: 1, + y: 2 + }, + c: [1, 2], + d: null, + 'b.x': 10 + }); + } + + function arrInvariant() { + expect(arr).to.eql( + [1, {a: [1, 2]}] + ); + } +}); + + diff --git a/test/dot-prop-immutable-toggle.spec.js b/test/dot-prop-immutable-toggle.spec.js new file mode 100644 index 0000000..4e8554f --- /dev/null +++ b/test/dot-prop-immutable-toggle.spec.js @@ -0,0 +1,33 @@ +var dotProp = require('..'); + +describe('dot-prop-immutable.toggle.spec.js', function () { + + var arr = [1, {a: false}]; + + var result; + describe('when have an array', () => { + + describe('toggle a value', () => { + + before(function () { + result = dotProp.toggle(arr, '1.a'); + }); + + + it('should toggle prop', () => { + expect(result).to.eql( + [1, {a: true}]); + }); + + it('invariant', arrInvariant); + }); + }); + + function arrInvariant() { + expect(arr).to.eql( + [1, {a: false}] + ); + } +}); + +