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 @@ -
+

Underscore-contrib (0.3.0)

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, _.chunkAll takes 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 _.nth is too tedious or you'd like to partially apply the index then Underscore-contrib offers any of _.flip2, _.fix or _.curryRight2 to 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 null if 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: _.renameKeys(obj:Object, keyMap:Object)

@@ -1444,9 +1457,21 @@

setPath

Sets the value of a property at any depth in obj based on the path described by the ks array. If any of the properties in the ks path don't exist, they will be created with defaultValue.

-

See _.updatePath about obj not 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, obj will +be cloned deeply.

+

+var obj = {};
+
+var plotinusObj = _.setPath(obj, "Plotinus", ["Platonism", "Neoplatonism"], {});
+
+obj;
+// => {}
+
+plotinusObj;
+// => { Platonism: { Neoplatonism: "Plotinus" } }
+
+obj === plotinusObj;
+// => false;

snapshot

Signature: _.snapshot(obj:Object)

@@ -1459,6 +1484,7 @@

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 ks array. The function fun is 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 obj excluding the value represented by the ks path. +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) {