Ensure _.merge ignores undefined values of source object properties. [closes #573]

This commit is contained in:
John-David Dalton
2014-06-03 09:26:47 -07:00
parent b2280b2d72
commit 7b4fd28ef1
2 changed files with 27 additions and 30 deletions

View File

@@ -1962,7 +1962,8 @@
* @returns {Object} Returns the destination object. * @returns {Object} Returns the destination object.
*/ */
function baseMerge(object, source, callback, stackA, stackB) { function baseMerge(object, source, callback, stackA, stackB) {
(isArrayLike(source) ? arrayEach : baseForOwn)(source, function(srcValue, key, source) { var isSrcArr = isArrayLike(source);
(isSrcArr ? arrayEach : baseForOwn)(source, function(srcValue, key, source) {
var isArr = srcValue && isArrayLike(srcValue), var isArr = srcValue && isArrayLike(srcValue),
isObj = srcValue && isPlainObject(srcValue), isObj = srcValue && isPlainObject(srcValue),
value = object[key]; value = object[key];
@@ -1972,10 +1973,9 @@
if (typeof result == 'undefined') { if (typeof result == 'undefined') {
result = srcValue; result = srcValue;
} }
if (typeof result != 'undefined') { if (isSrcArr || typeof result != 'undefined') {
value = result; object[key] = result;
} }
object[key] = value;
return; return;
} }
// avoid merging previously merged cyclic sources // avoid merging previously merged cyclic sources
@@ -1992,22 +1992,21 @@
var result = callback ? callback(value, srcValue, key, object, source) : undefined, var result = callback ? callback(value, srcValue, key, object, source) : undefined,
isShallow = typeof result != 'undefined'; isShallow = typeof result != 'undefined';
if (isShallow) { if (!isShallow) {
value = result; result = isArr
} else {
value = isArr
? (isArray(value) ? value : []) ? (isArray(value) ? value : [])
: (isPlainObject(value) ? value : {}); : (isPlainObject(value) ? value : {});
} }
// add `source` and associated `value` to the stack of traversed objects // add the source value to the stack of traversed objects
// and associate it with its merged value
stackA.push(srcValue); stackA.push(srcValue);
stackB.push(value); stackB.push(result);
// recursively merge objects and arrays (susceptible to call stack limits) // recursively merge objects and arrays (susceptible to call stack limits)
if (!isShallow) { if (!isShallow) {
baseMerge(value, srcValue, callback, stackA, stackB); baseMerge(result, srcValue, callback, stackA, stackB);
} }
object[key] = value; object[key] = result;
}); });
return object; return object;

View File

@@ -2580,22 +2580,6 @@
deepEqual(actual, { 'a': 1, 'b': 2, 'c': 3 }); deepEqual(actual, { 'a': 1, 'b': 2, 'c': 3 });
}); });
if (methodName == 'merge') {
test('`_.' + methodName + '` should treat sparse arrays as dense', 2, function() {
var array = Array(3);
array[0] = 1;
array[2] = 3;
var actual = func([], array),
expected = array.slice();
expected[1] = undefined;
ok('1' in actual);
deepEqual(actual, expected);
});
}
}); });
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -6365,6 +6349,20 @@
ok(actual.bar.b === actual.foo.b && actual.foo.b.foo.c === actual.foo.b.foo.c.foo.b.foo.c); ok(actual.bar.b === actual.foo.b && actual.foo.b.foo.c === actual.foo.b.foo.c.foo.b.foo.c);
}); });
test('should treat sources that are sparse arrays as dense', 2, function() {
var array = Array(3);
array[0] = 1;
array[2] = 3;
var actual = _.merge([], array),
expected = array.slice();
expected[1] = undefined;
ok('1' in actual);
deepEqual(actual, expected);
});
test('should not treat `arguments` objects as plain objects', 1, function() { test('should not treat `arguments` objects as plain objects', 1, function() {
var object = { var object = {
'args': args 'args': args
@@ -6389,8 +6387,8 @@
}); });
test('should not assign `undefined` values', 1, function() { test('should not assign `undefined` values', 1, function() {
var actual = _.merge({ 'a': 1 }, { 'a': undefined }); var actual = _.merge({ 'a': 1 }, { 'a': undefined, 'b': undefined });
strictEqual(actual.a, 1); deepEqual(actual, { 'a': 1 });
}); });
test('should handle merging if `callback` returns `undefined`', 1, function() { test('should handle merging if `callback` returns `undefined`', 1, function() {