diff --git a/docs/underscore.object.builders.js.md b/docs/underscore.object.builders.js.md index 74e27c2..2314be6 100644 --- a/docs/underscore.object.builders.js.md +++ b/docs/underscore.object.builders.js.md @@ -26,7 +26,7 @@ _.frequencies(citations); Returns a new object resulting from merging the passed objects. Objects are processed in order, so each will override properties of the same -name occurring in earlier arguments. +name occurring in earlier arguments. Returns `null` if called without arguments. @@ -148,3 +148,27 @@ obj === imperialObj; ``` -------------------------------------------------------------------------------- + +#### omitPath + +**Signature:** `_.omitPath(obj:Object, ks:String|Array)` + +Returns a copy of `obj` excluding the value represented by the `ks` path. +Path may be given as an array or as a dot-separated string. + +```javascript +var test = { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { + pepe: 9 + }, + pedro: 'pedro' + } +}; + +_.omitPath(test, 'dada.carlos.pepe'); +// => {foo: true, bar: false, baz: 42, dada: {carlos: {}, pedro: 'pedro'}} +``` diff --git a/index.html b/index.html index 4177872..82aeb2b 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,7 @@
-The brass buckles on Underscore's utility belt - a contributors' library for Underscore.
@@ -119,7 +119,7 @@chunkAll
_.chunkAll([0,1,2,3,4], 3); //=> , [[0,1,2],[3]]Also,
-_.chunkAlltakes an optional third argument signifying that paritions should be built from skipped regions:+_.chunkAll(_.range(1), 2, 4); +_.chunkAll(_.range(10), 2, 4); //=> [[0,1],[4,5],[8,9]]
cons
@@ -301,7 +301,7 @@nth
If wrapping a function around
_.nthis too tedious or you'd like to partially apply the index then Underscore-contrib offers any of_.flip2,_.fixor_.curryRight2to solve this.
partitionBy
-Signature:
+_.keep(array:Array, fun:Function)Signature:
_.partitionBy(array:Array, fun:Function)Takes an array and partitions it into sub-arrays as the given predicate changes truth sense.
_.partitionBy([1,2,2,3,1,1,5], _.isEven); @@ -1427,10 +1427,23 @@frequencies
merge
Signature:
-_.merge(obj1:Object[, obj:Object...])Merges two or more objects starting with the left-most and applying the keys -rightward.
-+_.merge({ a: "alpha" }, { b: "beta" }); -// => { a: "alpha", b: "beta" }Returns a new object resulting from merging the passed objects. Objects +are processed in order, so each will override properties of the same +name occurring in earlier arguments.
+Returns
+nullif called without arguments.var a = {a: "alpha"}; +var b = {b: "beta"}; + +var threeGreekLetters = _.merge(a, b, {g: "gamma"}); + +a; +// => {a: "alpha"} + +b; +// => {b: "beta"} + +threeGreekLetters; +// => { a: "alpha", b: "beta", g: "gamma" }
renameKeys
Signature:
@@ -1444,9 +1457,21 @@_.renameKeys(obj:Object, keyMap:Object)setPath
Sets the value of a property at any depth in
-objbased on the path described by theksarray. If any of the properties in thekspath don't exist, they will be created withdefaultValue.See
-_.updatePathaboutobjnot being mutated in the process by cloning it.+_.setPath({}, "Plotinus", ["Platonism", "Neoplatonism"], {}); -// => { Platonism: { Neoplatonism: "Plotinus" } }Note that the original object will not be mutated. Instead,
+objwill +be cloned deeply.+var obj = {}; + +var plotinusObj = _.setPath(obj, "Plotinus", ["Platonism", "Neoplatonism"], {}); + +obj; +// => {} + +plotinusObj; +// => { Platonism: { Neoplatonism: "Plotinus" } } + +obj === plotinusObj; +// => false;
snapshot
Signature:
@@ -1459,6 +1484,7 @@_.snapshot(obj:Object)snapshot
schools === _.snapshot(schools); // => false
+updatePath
Signature:
_.updatePath(obj:Object, fun:Function, ks:Array, defaultValue:Any)Updates the value at any depth in a nested object based on the path described by the
ksarray. The functionfunis called with the current value and is @@ -1486,6 +1512,7 @@snapshot
obj === imperialObj; // => false
object.selectors
Functions to select values from an object.
@@ -1619,7 +1646,7 @@omitWhen
shakespere: "England" }; -_.omitWhen(obj, function (country) { return country == "England" }); +_.omitWhen(playwrights, function (country) { return country == "England" }); // => { euripedes: "Greece" }
pickWhen
@@ -1632,7 +1659,7 @@pickWhen
shakespere: "England" }; -_.pickWhen(obj, function (country) { return country == "England" }); +_.pickWhen(playwrights, function (country) { return country == "England" }); // => { shakespeare: "England" }
selectKeys
@@ -1646,6 +1673,25 @@selectKeys
_.selectKeys(philosopherCities, ["Plato", "Plotinus"]); // => { Plato: "Athens", Plotinus: "Rome" } +omitPath
+Signature: `_.omitPath(obj:Object, ks:String|Array);
+Returns a copy of
+objexcluding the value represented by thekspath. +Path may be given as an array or as a dot-separated string. +If the path contains an array, the value of the path will be removed from all the array elements.var test = { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { + pepe: 9 + }, + pedro: 'pedro' + } +}; + +_.omitPath(test, 'dada.carlos.pepe'); +// => {foo: true, bar: false, baz: 42, dada: {carlos: {}, pedro: 'pedro'}}util.existential
Functions which deal with whether a value "exists."
@@ -2101,7 +2147,7 @@trampoline
function isEvenNaive (num) { if (num === 0) return true; if (num === 1) return false; - return isEvenNaive(num - 2); + return isEvenNaive(Math.abs(num) - 2); } isEvenNaive(99999); diff --git a/test/object.builders.js b/test/object.builders.js index 061cda9..0436d83 100644 --- a/test/object.builders.js +++ b/test/object.builders.js @@ -52,7 +52,7 @@ $(document).ready(function() { assert.deepEqual(_.setPath(obj, 9, ['a', 'b', 'c']), {a: {b: {c: 9, d: 108}}}, ''); assert.deepEqual(_.setPath(ary, 9, [1, 1, 0]), ['a', ['b', [9, 'd'], 'e']], ''); - assert.deepEqual(_.setPath(nest, 9, [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], ''); + assert.deepEqual(_.setPath(nest, 9, [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], ''); assert.deepEqual(_.setPath(obj, 9, 'a'), {a: 9}, ''); assert.deepEqual(_.setPath(ary, 9, 1), ['a', 9], ''); @@ -69,7 +69,7 @@ $(document).ready(function() { assert.deepEqual(_.updatePath(obj, _.always(9), ['a', 'b', 'c']), {a: {b: {c: 9, d: 108}}}, ''); assert.deepEqual(_.updatePath(ary, _.always(9), [1, 1, 0]), ['a', ['b', [9, 'd'], 'e']], ''); - assert.deepEqual(_.updatePath(nest, _.always(9), [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], ''); + assert.deepEqual(_.updatePath(nest, _.always(9), [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], ''); assert.deepEqual(_.updatePath(obj, _.always(9), 'a'), {a: 9}, ''); assert.deepEqual(_.updatePath(ary, _.always(9), 1), ['a', 9], ''); @@ -79,4 +79,114 @@ $(document).ready(function() { assert.deepEqual(nest, [1, {a: 2, b: [3,4], c: 5}, 6], 'should not modify the original nested structure'); }); + QUnit.test("omitPath", function(assert){ + var a = { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { pepe: 9 }, + pedro: 'pedro', + list: [ + {file: '..', more: {other: { a: 1, b: 2}}, name: 'aa'}, + {file: '..', name: 'bb'} + ] + } + }; + + assert.deepEqual(_.omitPath(a, 'dada.carlos.pepe'), { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: {}, + pedro: 'pedro', + list: [ + {file: '..', more: {other: { a: 1, b: 2}} , name: 'aa'}, + {file: '..', name: 'bb'} + ] + } + }, "should return an object without the value that represent the path"); + + assert.deepEqual(_.omitPath(a, 'dada.carlos'), { + foo: true, + bar: false, + baz: 42, + dada: { + pedro: 'pedro', + list: [ + {file: '..', more: {other: { a: 1, b: 2}} , name: 'aa'}, + {file: '..', name: 'bb'} + ] + } + }, "should return an object without the value that represent the path"); + + assert.deepEqual(_.omitPath(a, ''), { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { pepe: 9 }, + pedro: 'pedro', + list: [ + {file: '..', more: {other: { a: 1, b: 2}} , name: 'aa'}, + {file: '..', name: 'bb'} + ] + } + }, "should return the whole object because the path is empty"); + + assert.deepEqual(_.omitPath(a, 'dada.list.0.file'), { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { pepe: 9 }, + pedro: 'pedro', + list: [ + {name: 'aa', more: {other: { a: 1, b: 2}}}, + {file: '..', name: 'bb'} + ] + } + }, "should return an object without the value in an element of the list"); + + assert.deepEqual(_.omitPath(a, 'dada.list.1.name'), { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { pepe: 9 }, + pedro: 'pedro', + list: [ + {name: 'aa', file: '..', more: {other: { a: 1, b: 2}}}, + {file: '..'} + ] + } + }, "should return an object without the value in each object of the list"); + + assert.deepEqual(_.omitPath(a, 'dada.list'), { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { pepe: 9 }, + pedro: 'pedro' + } + }, "should return an object without the list"); + + assert.deepEqual(_.omitPath(a, 'dada.list.0.more.other.a'), { + foo: true, + bar: false, + baz: 42, + dada: { + carlos: { pepe: 9 }, + pedro: 'pedro', + list: [ + {file: '..', more: {other: { b: 2}} , name: 'aa'}, + {file: '..', name: 'bb'} + ] + } + }, + "should return an object without the value inside the values of the list" + ); + }); }); diff --git a/underscore.object.builders.js b/underscore.object.builders.js index 613f9c3..cfa1a00 100644 --- a/underscore.object.builders.js +++ b/underscore.object.builders.js @@ -106,6 +106,16 @@ return ret; }, + // Returns an object excluding the value represented by the path + omitPath: function(obj, ks, copy){ + if (!obj) return copy; + if (typeof ks == "string") ks = ks.split("."); + if (!copy) copy = obj = _.snapshot(obj); + if (ks.length > 1) return _.omitPath(obj[ks[0]], _.tail(ks), copy); + delete obj[ks[0]]; + return copy; + }, + // Sets the value at any depth in a nested object based on the // path described by the keys given. setPath: function(obj, value, ks, defaultValue) {