From 515cfd48d8416d95b631622cc2ac1f82e8ca68d6 Mon Sep 17 00:00:00 2001 From: Joshua Piccari Date: Wed, 25 Mar 2015 22:52:25 -0700 Subject: [PATCH] Add `_.resultDeep`. --- lodash.src.js | 40 ++++++++++++++++++++++++++++++++++++++++ test/test.js | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/lodash.src.js b/lodash.src.js index 2ebce5586..2b6ee5457 100644 --- a/lodash.src.js +++ b/lodash.src.js @@ -9796,6 +9796,45 @@ return isFunction(value) ? value.call(object) : value; } + /** + * Resolves the value of `keyPath` on `object`. If any values along `keyPath` + * is a function it is invoked with the `this` binding of `object` and its + * result is used to resolve the remainder of the `keyPath` rather than the + * function itself. If at any point along the `keyPath` the value is + * `undefined` the `defaultValue` is returned. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {array} keyPath The key path of the properties to resolve. + * @param {*} [defaultValue] The value returned if the property value + * resolves to `undefined`. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { + * 'user': 'fred', + * 'dates': { 'birthdate': '03/17/1988', anniversary: '07/29/2012' } + * }; + * + * _.resultDeep(object, ['user']); + * // => 'fred' + * + * _.resultDeep(object, ['user', 'dates', 'anniversary']); + * // => '07/29/2012' + * + * _.resultDeep(object, ['status', 'timestamp'], _.constant('busy')); + * // => 'busy' + */ + function resultDeep(object, keyPath, defaultValue) { + arrayEach(dropRight(keyPath), function(key) { + object = result(object, key); + return object != null; + }); + return result(object, last(keyPath), defaultValue); + } + /** * An alternative to `_.reduce`; this method transforms `object` to a new * `accumulator` object which is the result of running each of its own enumerable @@ -11732,6 +11771,7 @@ lodash.reduceRight = reduceRight; lodash.repeat = repeat; lodash.result = result; + lodash.resultDeep = resultDeep; lodash.runInContext = runInContext; lodash.size = size; lodash.snakeCase = snakeCase; diff --git a/test/test.js b/test/test.js index d71befd8b..15b6ec8e9 100644 --- a/test/test.js +++ b/test/test.js @@ -12501,6 +12501,52 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.resultDeep'); + + (function() { + var object = { + 'a': { d: 1 }, + 'b': null, + 'c': function() { return this.a; } + }; + + test('should resolve property values', 4, function() { + strictEqual(_.resultDeep(object, ['a', 'd']), 1); + strictEqual(_.resultDeep(object, ['b']), null); + strictEqual(_.resultDeep(object, ['c', 'd']), 1); + strictEqual(_.resultDeep(object, ['d']), undefined); + }); + + test('should return `undefined` when `object` is nullish', 2, function() { + strictEqual(_.resultDeep(null, ['a']), undefined); + strictEqual(_.resultDeep(undefined, ['a']), undefined); + }); + + test('should return the specified default value for undefined properties', 1, function() { + var values = empties.concat(true, new Date, 1, /x/, 'a'); + + var expected = _.transform(values, function(result, value) { + result.push(value, value); + }); + + var actual = _.transform(values, function(result, value) { + result.push( + _.resultDeep(object, ['d'], value), + _.resultDeep(null, ['d'], value) + ); + }); + + deepEqual(actual, expected); + }); + + test('should execute default function values', 1, function() { + var actual = _.resultDeep(object, ['d'], object.c); + strictEqual(actual, object.a); + }); + }()); + + /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.rest'); (function() { @@ -16535,7 +16581,7 @@ var acceptFalsey = _.difference(allMethods, rejectFalsey); - test('should accept falsey arguments', 213, function() { + test('should accept falsey arguments', 214, function() { var emptyArrays = _.map(falsey, _.constant([])), isExposed = '_' in root, oldDash = root._;