diff --git a/lodash.js b/lodash.js index ceb97a362..84c7eed10 100644 --- a/lodash.js +++ b/lodash.js @@ -2959,6 +2959,21 @@ return result; } + /** + * The base implementation of `_.unset`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + */ + function baseUnset(object, path) { + path = isKey(path, object) ? [path + ''] : toPath(path); + object = parent(object, path); + var key = last(path); + return (object != null && has(object, key)) ? delete object[key] : true; + } + /** * The base implementation of methods like `_.dropWhile` and `_.takeWhile` * without support for callback shorthands. @@ -9843,6 +9858,34 @@ return accumulator; } + /** + * Removes the property at `path` of `object`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 7 } }] }; + * _.unset(object, 'a[0].b.c'); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + * + * _.unset(object, 'a[0].b.c'); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + */ + function unset(object, path) { + return object == null ? true : baseUnset(object, path); + } + /** * Creates an array of the own enumerable property values of `object`. * @@ -11721,6 +11764,7 @@ lodash.union = union; lodash.uniq = uniq; lodash.uniqBy = uniqBy; + lodash.unset = unset; lodash.unzip = unzip; lodash.unzipWith = unzipWith; lodash.values = values; diff --git a/test/test.js b/test/test.js index 9bf8d5a7f..647652535 100644 --- a/test/test.js +++ b/test/test.js @@ -16106,6 +16106,90 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.unset'); + + (function() { + test('should unset property on given `object`', 4, function() { + _.each(['a', ['a']], function(path) { + var object = { 'a': 1, 'c': 2 }; + strictEqual(_.unset(object, path), true); + deepEqual(object, { 'c': 2 }); + }); + }); + + test('should unset deep property on given `object`', 4, function() { + _.each(['a.b.c', ['a', 'b', 'c']], function(path) { + var object = { 'a': { 'b': { 'c': null } } }; + strictEqual(_.unset(object, path), true); + deepEqual(object, { 'a': { 'b': {} } }); + }); + }); + + test('should handle complex paths', 4, function() { + var paths = [ + 'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g', + ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g'] + ]; + + _.each(paths, function(path) { + var object = { 'a': { '-1.23': { '["b"]': { 'c': { "['d']": { '\ne\n': { 'f': { 'g': 8 } } } } } } } }; + strictEqual(_.unset(object, path), true); + + ok(!('g' in object.a[-1.23]['["b"]'].c["['d']"]['\ne\n'].f)); + }); + }); + + test('should return `true` for nonexistent paths', 5, function() { + var object = { 'a': { 'b': { 'c': null } } }; + + _.each(['z', 'a.z', 'a.b.z', 'a.b.c.z'], function(path) { + strictEqual(_.unset(object, path), true); + }); + + deepEqual(object, { 'a': { 'b': { 'c': null } } }); + }); + + test('should not error when `object` is nullish', 1, function() { + var values = [null, undefined], + expected = [[true, true], [true, true]]; + + var actual = _.map(values, function(value) { + try { + return [_.unset(value, 'a.b'), _.unset(value, ['a', 'b'])]; + } catch(e) { + return e.message; + } + }); + + deepEqual(actual, expected); + }); + + test('should follow `path` over non-plain objects', 2, function() { + function Foo() {}; + Foo.prototype.a = 1; + + strictEqual(_.unset(Foo, 'prototype.a'), true); + strictEqual(Foo.prototype.a, undefined); + }); + + test('should return `false` for non-configurable properties', 1, function() { + var object = {}; + + if (defineProperty) { + defineProperty(object, 'a', { + 'configurable': false, + 'value': null + }); + strictEqual(_.unset(object, 'a'), false); + } + else { + skipTest(); + } + }); + }()); + + /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.unzipWith'); (function() { @@ -17438,7 +17522,7 @@ var acceptFalsey = _.difference(allMethods, rejectFalsey); - test('should accept falsey arguments', 228, function() { + test('should accept falsey arguments', 229, function() { var emptyArrays = _.map(falsey, _.constant([])); _.each(acceptFalsey, function(methodName) {