diff --git a/lodash.src.js b/lodash.src.js index 2b6ee5457..1a61d001d 100644 --- a/lodash.src.js +++ b/lodash.src.js @@ -2598,6 +2598,31 @@ }; } + /** + * The base implementation of `_.propertyDeep` which does not coerce `keys` to strings. + * + * @private + * @param {string[]} keys Keys that specify the property to get. + * @returns {Function} Returns the new function. + */ + function basePropertyDeep(keys) { + return function(object) { + if (object == null) { + return undefined; + } + var index = -1, + length = keys.length; + + while (++index < length) { + object = object[keys[index]]; + if (object == null) { + return undefined; + } + } + return object; + }; + } + /** * The base implementation of `_.pullAt` without support for individual * index arguments and capturing the removed elements. @@ -11243,6 +11268,33 @@ return baseProperty(key + ''); } + /** + * This function is like `_.property` except it takes a `keys` array to get nested property. + * @static + * @memberOf _ + * @category Utility + * @param {string[]} keys Keys that specify the property to get. + * @returns {Function} Returns the new function. + * @example + * + * var users = [ + * { 'user': { 'name': 'fred' } }, + * { 'user': { 'name': 'barney', 'age': 36 } } + * ]; + * + * var getName = _.propertyDeep(['user', 'name']); + * var getAge = _.propertyDeep(['user', 'age']); + * + * _.map(users, getName); + * // => ['fred', 'barney'] + * + * _.map(users, getAge); + * // => [undefined, 36] + */ + function propertyDeep(keys) { + return basePropertyDeep(arrayMap(keys || [], baseToString)); + } + /** * The opposite of `_.property`; this method creates a function which returns * the property value of a given key on `object`. @@ -11268,6 +11320,36 @@ }; } + /** + * The opposite of `_.propertyDeep`; this method creates a function which returns + * the property value of a given key path on `object`. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} object The object to inspect. + * @returns {Function} Returns the new function. + * @example + * + * var users = [ + * { user: 'fredrick', nickname: 'fred' }, + * { user: 'bernard', nickname: 'barney' } + * ]; + * + * var propertyOfUsers = _.propertyDeepOf(users); + * + * propertyOfUsers([0, 'nickname']); + * // => 'fred' + * + * propertyOfUsers([1, 'name']); + * // => 'bernard' + */ + function propertyDeepOf(object) { + return function(keyPath) { + return resultDeep(object, keyPath); + }; + } + /** * Creates an array of numbers (positive and/or negative) progressing from * `start` up to, but not including, `end`. If `end` is not specified it is @@ -11651,7 +11733,9 @@ lodash.pick = pick; lodash.pluck = pluck; lodash.property = property; + lodash.propertyDeep = propertyDeep; lodash.propertyOf = propertyOf; + lodash.propertyDeepOf = propertyDeepOf; lodash.pull = pull; lodash.pullAt = pullAt; lodash.range = range; diff --git a/test/test.js b/test/test.js index 15b6ec8e9..5ab7df282 100644 --- a/test/test.js +++ b/test/test.js @@ -11588,6 +11588,58 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.propertyDeep'); + + (function() { + test('should create a function that plucks a property value of a given object', 3, function() { + var object = { 'a': [1, 2], 'b': 3 }, + prop = _.propertyDeep(['a', 1]); + + strictEqual(prop.length, 1); + strictEqual(prop(object), 2); + + prop = _.propertyDeep(['b']); + strictEqual(prop(object), 3); + }); + + test('should work with non-string `prop` arguments', 1, function() { + var prop = _.propertyDeep([1]); + strictEqual(prop([1, 2, 3]), 2); + }); + + test('should return a function even for falsey values', 1, function() { + var pass = true; + + _.each(falsey, function(keys) { + pass = typeof _.propertyDeep(keys) == 'function' + }); + + ok(pass); + }); + + test('should pluck inherited property values', 1, function() { + function Foo() { this.a = 1; } + Foo.prototype.b = 2; + + var prop = _.propertyDeep(['b']); + strictEqual(prop(new Foo), 2); + }); + + test('should work when `object` is nullish', 1, function() { + var values = [, null, undefined], + expected = _.map(values, _.constant(undefined)); + + var actual = _.map(values, function(value, index) { + var prop = _.propertyDeep(['a']); + return index ? prop(value) : prop(); + }); + + deepEqual(actual, expected); + }); + }()); + + /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.propertyOf'); (function() { @@ -11623,6 +11675,41 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.propertyDeepOf'); + + (function() { + test('should create a function that plucks a property value of a given key', 3, function() { + var object = { 'a': 1, 'b': 2 }, + propOf = _.propertyDeepOf(object); + + strictEqual(propOf.length, 1); + strictEqual(propOf(['a']), 1); + strictEqual(propOf(['b']), 2); + }); + + test('should pluck inherited property values', 1, function() { + function Foo() { this.a = 1; } + Foo.prototype.b = 2; + + var propOf = _.propertyDeepOf(new Foo); + strictEqual(propOf(['b']), 2); + }); + + test('should work when `object` is nullish', 1, function() { + var values = [, null, undefined], + expected = _.map(values, _.constant(undefined)); + + var actual = _.map(values, function(value, index) { + var propOf = index ? _.propertyDeepOf(value) : _.propertyDeepOf(); + return propOf(['a']); + }); + + deepEqual(actual, expected); + }); + }()); + + /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.pull'); (function() { @@ -16581,7 +16668,7 @@ var acceptFalsey = _.difference(allMethods, rejectFalsey); - test('should accept falsey arguments', 214, function() { + test('should accept falsey arguments', 216, function() { var emptyArrays = _.map(falsey, _.constant([])), isExposed = '_' in root, oldDash = root._;