mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-31 23:37:49 +00:00
Add _.assignWith, _.cloneDeepWith, _.cloneWith, _.extendWith, _.isEqualWith, _.isMatchWith, and _.mergeWith.
This commit is contained in:
390
lodash.src.js
390
lodash.src.js
@@ -2342,6 +2342,48 @@
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.merge` without support multiple sources.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The destination object.
|
||||
* @param {Object} source The source object.
|
||||
* @param {Function} [customizer] The function to customize merged values.
|
||||
* @param {Array} [stackA=[]] Tracks traversed source objects.
|
||||
* @param {Array} [stackB=[]] Associates values with source counterparts.
|
||||
* @returns {Object} Returns `object`.
|
||||
*/
|
||||
function baseMerge(object, source, customizer, stackA, stackB) {
|
||||
var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
|
||||
props = isSrcArr ? undefined : keysIn(source);
|
||||
|
||||
arrayEach(props || source, function(srcValue, key) {
|
||||
if (props) {
|
||||
key = srcValue;
|
||||
srcValue = source[key];
|
||||
}
|
||||
if (isObjectLike(srcValue)) {
|
||||
stackA || (stackA = []);
|
||||
stackB || (stackB = []);
|
||||
baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
|
||||
}
|
||||
else {
|
||||
var value = object[key],
|
||||
result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
|
||||
isCommon = result === undefined;
|
||||
|
||||
if (isCommon) {
|
||||
result = srcValue;
|
||||
}
|
||||
if ((result !== undefined || (isSrcArr && !(key in object))) &&
|
||||
(isCommon || (result === result ? (result !== value) : (value === value)))) {
|
||||
object[key] = result;
|
||||
}
|
||||
}
|
||||
});
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* A specialized version of `baseMerge` for arrays and objects which performs
|
||||
* deep merges and tracks traversed objects enabling objects with circular
|
||||
@@ -2959,7 +3001,7 @@
|
||||
* @private
|
||||
* @param {Object} source The object to copy properties from.
|
||||
* @param {Array} props The property names to copy.
|
||||
* @param {Function} customizer The function to customize copied values.
|
||||
* @param {Function} [customizer] The function to customize copied values.
|
||||
* @param {Object} [object={}] The object to copy properties to.
|
||||
* @returns {Object} Returns `object`.
|
||||
*/
|
||||
@@ -2972,9 +3014,10 @@
|
||||
while (++index < length) {
|
||||
var key = props[index],
|
||||
value = object[key],
|
||||
result = customizer(value, source[key], key, object, source);
|
||||
result = customizer ? customizer(value, source[key], key, object, source) : source[key];
|
||||
|
||||
if ((result === result ? (result !== value) : (value === value)) ||
|
||||
if (!customizer ||
|
||||
(result === result ? (result !== value) : (value === value)) ||
|
||||
(value === undefined && !(key in object))) {
|
||||
object[key] = result;
|
||||
}
|
||||
@@ -4067,7 +4110,7 @@
|
||||
return sourceValue;
|
||||
}
|
||||
return isObject(objectValue)
|
||||
? merge(objectValue, sourceValue, mergeDefaults)
|
||||
? mergeWith(objectValue, sourceValue, mergeDefaults)
|
||||
: objectValue;
|
||||
}
|
||||
|
||||
@@ -7713,10 +7756,7 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Creates a shallow clone of `value`. If `customizer` is provided it's invoked
|
||||
* to produce the cloned value. If `customizer` returns `undefined` cloning
|
||||
* is handled by the method instead. The `customizer` is invoked with up to
|
||||
* three arguments; (value [, index|key, object]).
|
||||
* Creates a shallow clone of `value`.
|
||||
*
|
||||
* **Note:** This method is loosely based on the
|
||||
* [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
|
||||
@@ -7729,7 +7769,6 @@
|
||||
* @memberOf _
|
||||
* @category Lang
|
||||
* @param {*} value The value to clone.
|
||||
* @param {Function} [customizer] The function to customize cloning.
|
||||
* @returns {*} Returns the cloned value.
|
||||
* @example
|
||||
*
|
||||
@@ -7741,8 +7780,25 @@
|
||||
* var shallow = _.clone(users);
|
||||
* shallow[0] === users[0];
|
||||
* // => true
|
||||
*/
|
||||
function clone(value) {
|
||||
return baseClone(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is like `_.clone` except that it accepts `customizer` which
|
||||
* is invoked to produce the cloned value. If `customizer` returns `undefined`
|
||||
* cloning is handled by the method instead. The `customizer` is invoked with
|
||||
* up to three arguments; (value [, index|key, object]).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Lang
|
||||
* @param {*} value The value to clone.
|
||||
* @param {Function} [customizer] The function to customize cloning.
|
||||
* @returns {*} Returns the cloned value.
|
||||
* @example
|
||||
*
|
||||
* // using a customizer callback
|
||||
* var el = _.clone(document.body, function(value) {
|
||||
* if (_.isElement(value)) {
|
||||
* return value.cloneNode(false);
|
||||
@@ -7756,10 +7812,8 @@
|
||||
* el.childNodes.length;
|
||||
* // => 0
|
||||
*/
|
||||
function clone(value, customizer) {
|
||||
return typeof customizer == 'function'
|
||||
? baseClone(value, false, customizer)
|
||||
: baseClone(value);
|
||||
function cloneWith(value, customizer) {
|
||||
return baseClone(value, false, customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7769,7 +7823,6 @@
|
||||
* @memberOf _
|
||||
* @category Lang
|
||||
* @param {*} value The value to recursively clone.
|
||||
* @param {Function} [customizer] The function to customize cloning.
|
||||
* @returns {*} Returns the deep cloned value.
|
||||
* @example
|
||||
*
|
||||
@@ -7781,8 +7834,22 @@
|
||||
* var deep = _.cloneDeep(users);
|
||||
* deep[0] === users[0];
|
||||
* // => false
|
||||
*/
|
||||
function cloneDeep(value) {
|
||||
return baseClone(value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is like `_.cloneWith` except that it recursively clones `value`.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Lang
|
||||
* @param {*} value The value to recursively clone.
|
||||
* @param {Function} [customizer] The function to customize cloning.
|
||||
* @returns {*} Returns the deep cloned value.
|
||||
* @example
|
||||
*
|
||||
* // using a customizer callback
|
||||
* var el = _.cloneDeep(document.body, function(value) {
|
||||
* if (_.isElement(value)) {
|
||||
* return value.cloneNode(true);
|
||||
@@ -7796,10 +7863,8 @@
|
||||
* el.childNodes.length;
|
||||
* // => 20
|
||||
*/
|
||||
function cloneDeep(value, customizer) {
|
||||
return typeof customizer == 'function'
|
||||
? baseClone(value, true, customizer)
|
||||
: baseClone(value, true);
|
||||
function cloneDeepWith(value, customizer) {
|
||||
return baseClone(value, true, customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7991,16 +8056,12 @@
|
||||
|
||||
/**
|
||||
* Performs a deep comparison between two values to determine if they are
|
||||
* equivalent. If `customizer` is provided it's invoked to compare values.
|
||||
* If `customizer` returns `undefined` comparisons are handled by the method
|
||||
* instead. The `customizer` is invoked with up to three arguments:
|
||||
* (value, other [, index|key]).
|
||||
* equivalent.
|
||||
*
|
||||
* **Note:** This method supports comparing arrays, booleans, `Date` objects,
|
||||
* numbers, `Object` objects, regexes, and strings. Objects are compared by
|
||||
* their own, not inherited, enumerable properties. Functions and DOM nodes
|
||||
* are **not** supported. Provide a customizer function to extend support
|
||||
* for comparing other values.
|
||||
* are **not** supported.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
@@ -8008,7 +8069,6 @@
|
||||
* @category Lang
|
||||
* @param {*} value The value to compare.
|
||||
* @param {*} other The other value to compare.
|
||||
* @param {Function} [customizer] The function to customize comparisons.
|
||||
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
|
||||
* @example
|
||||
*
|
||||
@@ -8020,19 +8080,38 @@
|
||||
*
|
||||
* _.isEqual(object, other);
|
||||
* // => true
|
||||
*/
|
||||
function isEqual(value, other) {
|
||||
return baseIsEqual(value, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is like `_.isEqual` except that it accepts `customizer` which
|
||||
* is invoked to compare values. If `customizer` returns `undefined` comparisons
|
||||
* are handled by the method instead. The `customizer` is invoked with up to
|
||||
* three arguments: (value, other [, index|key]).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @alias eq
|
||||
* @category Lang
|
||||
* @param {*} value The value to compare.
|
||||
* @param {*} other The other value to compare.
|
||||
* @param {Function} [customizer] The function to customize comparisons.
|
||||
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
|
||||
* @example
|
||||
*
|
||||
* // using a customizer callback
|
||||
* var array = ['hello', 'goodbye'];
|
||||
* var other = ['hi', 'goodbye'];
|
||||
*
|
||||
* _.isEqual(array, other, function(value, other) {
|
||||
* _.isEqualWith(array, other, function(value, other) {
|
||||
* if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
* // => true
|
||||
*/
|
||||
function isEqual(value, other, customizer) {
|
||||
function isEqualWith(value, other, customizer) {
|
||||
customizer = typeof customizer == 'function' ? customizer : undefined;
|
||||
var result = customizer ? customizer(value, other) : undefined;
|
||||
return result === undefined ? baseIsEqual(value, other, customizer) : !!result;
|
||||
@@ -8142,10 +8221,7 @@
|
||||
|
||||
/**
|
||||
* Performs a deep comparison between `object` and `source` to determine if
|
||||
* `object` contains equivalent property values. If `customizer` is provided
|
||||
* it's invoked to compare values. If `customizer` returns `undefined`
|
||||
* comparisons are handled by the method instead. The `customizer` is invoked
|
||||
* with three arguments: (value, other, index|key).
|
||||
* `object` contains equivalent property values.
|
||||
*
|
||||
* **Note:** This method supports comparing properties of arrays, booleans,
|
||||
* `Date` objects, numbers, `Object` objects, regexes, and strings. Functions
|
||||
@@ -8157,7 +8233,6 @@
|
||||
* @category Lang
|
||||
* @param {Object} object The object to inspect.
|
||||
* @param {Object} source The object of property values to match.
|
||||
* @param {Function} [customizer] The function to customize comparisons.
|
||||
* @returns {boolean} Returns `true` if `object` is a match, else `false`.
|
||||
* @example
|
||||
*
|
||||
@@ -8168,8 +8243,26 @@
|
||||
*
|
||||
* _.isMatch(object, { 'age': 36 });
|
||||
* // => false
|
||||
*/
|
||||
function isMatch(object, source) {
|
||||
return baseIsMatch(object, getMatchData(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is like `_.isMatch` except that it accepts `customizer` which
|
||||
* is invoked to compare values. If `customizer` returns `undefined` comparisons
|
||||
* are handled by the method instead. The `customizer` is invoked with three
|
||||
* arguments: (value, other, index|key).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Lang
|
||||
* @param {Object} object The object to inspect.
|
||||
* @param {Object} source The object of property values to match.
|
||||
* @param {Function} [customizer] The function to customize comparisons.
|
||||
* @returns {boolean} Returns `true` if `object` is a match, else `false`.
|
||||
* @example
|
||||
*
|
||||
* // using a customizer callback
|
||||
* var object = { 'greeting': 'hello' };
|
||||
* var source = { 'greeting': 'hi' };
|
||||
*
|
||||
@@ -8178,7 +8271,7 @@
|
||||
* });
|
||||
* // => true
|
||||
*/
|
||||
function isMatch(object, source, customizer) {
|
||||
function isMatchWith(object, source, customizer) {
|
||||
customizer = typeof customizer == 'function' ? customizer : undefined;
|
||||
return baseIsMatch(object, getMatchData(source), customizer);
|
||||
}
|
||||
@@ -8528,89 +8621,9 @@
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Recursively merges own enumerable properties of the source object(s), that
|
||||
* don't resolve to `undefined` into the destination object. Subsequent sources
|
||||
* overwrite property assignments of previous sources. If `customizer` is
|
||||
* provided it's invoked to produce the merged values of the destination and
|
||||
* source properties. If `customizer` returns `undefined` merging is handled
|
||||
* by the method instead. The `customizer` is invoked with five arguments:
|
||||
* (objectValue, sourceValue, key, object, source).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Object
|
||||
* @param {Object} object The destination object.
|
||||
* @param {...Object} [sources] The source objects.
|
||||
* @param {Function} [customizer] The function to customize assigned values.
|
||||
* @returns {Object} Returns `object`.
|
||||
* @example
|
||||
*
|
||||
* var users = {
|
||||
* 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
|
||||
* };
|
||||
*
|
||||
* var ages = {
|
||||
* 'data': [{ 'age': 36 }, { 'age': 40 }]
|
||||
* };
|
||||
*
|
||||
* _.merge(users, ages);
|
||||
* // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
|
||||
*
|
||||
* // using a customizer callback
|
||||
* var object = {
|
||||
* 'fruits': ['apple'],
|
||||
* 'vegetables': ['beet']
|
||||
* };
|
||||
*
|
||||
* var other = {
|
||||
* 'fruits': ['banana'],
|
||||
* 'vegetables': ['carrot']
|
||||
* };
|
||||
*
|
||||
* _.merge(object, other, function(a, b) {
|
||||
* if (_.isArray(a)) {
|
||||
* return a.concat(b);
|
||||
* }
|
||||
* });
|
||||
* // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
|
||||
*/
|
||||
var merge = createAssigner(function baseMerge(object, source, customizer, stackA, stackB) {
|
||||
var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
|
||||
props = isSrcArr ? undefined : keysIn(source);
|
||||
|
||||
arrayEach(props || source, function(srcValue, key) {
|
||||
if (props) {
|
||||
key = srcValue;
|
||||
srcValue = source[key];
|
||||
}
|
||||
if (isObjectLike(srcValue)) {
|
||||
stackA || (stackA = []);
|
||||
stackB || (stackB = []);
|
||||
baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
|
||||
}
|
||||
else {
|
||||
var value = object[key],
|
||||
result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
|
||||
isCommon = result === undefined;
|
||||
|
||||
if (isCommon) {
|
||||
result = srcValue;
|
||||
}
|
||||
if ((result !== undefined || (isSrcArr && !(key in object))) &&
|
||||
(isCommon || (result === result ? (result !== value) : (value === value)))) {
|
||||
object[key] = result;
|
||||
}
|
||||
}
|
||||
});
|
||||
return object;
|
||||
});
|
||||
|
||||
/**
|
||||
* Assigns own enumerable properties of source object(s) to the destination
|
||||
* object. Subsequent sources overwrite property assignments of previous sources.
|
||||
* If `customizer` is provided it's invoked to produce the assigned values.
|
||||
* The `customizer` is invoked with five arguments: (objectValue, sourceValue, key, object, source).
|
||||
*
|
||||
* **Note:** This method mutates `object` and is based on
|
||||
* [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign).
|
||||
@@ -8620,28 +8633,41 @@
|
||||
* @category Object
|
||||
* @param {Object} object The destination object.
|
||||
* @param {...Object} [sources] The source objects.
|
||||
* @param {Function} [customizer] The function to customize assigned values.
|
||||
* @returns {Object} Returns `object`.
|
||||
* @example
|
||||
*
|
||||
* _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
|
||||
* // => { 'user': 'fred', 'age': 40 }
|
||||
*/
|
||||
var assign = createAssigner(function(object, source) {
|
||||
copyObject(source, keys(source), object);
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is like `_.assign` except that it accepts `customizer` which
|
||||
* is invoked to produce the assigned values. The `customizer` is invoked
|
||||
* with five arguments: (objectValue, sourceValue, key, object, source).
|
||||
*
|
||||
* // using a customizer callback
|
||||
* var defaults = _.partialRight(_.assign, function(value, other) {
|
||||
* **Note:** This method mutates `object`.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Object
|
||||
* @param {Object} object The destination object.
|
||||
* @param {...Object} sources The source objects.
|
||||
* @param {Function} [customizer] The function to customize assigned values.
|
||||
* @returns {Object} Returns `object`.
|
||||
* @example
|
||||
*
|
||||
* var defaults = _.partialRight(_.assignWith, function(value, other) {
|
||||
* return _.isUndefined(value) ? other : value;
|
||||
* });
|
||||
*
|
||||
* defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
|
||||
* // => { 'user': 'barney', 'age': 36 }
|
||||
*/
|
||||
var assign = createAssigner(function(object, source, customizer) {
|
||||
var props = keys(source);
|
||||
if (customizer) {
|
||||
copyObjectWith(source, props, customizer, object);
|
||||
} else {
|
||||
copyObject(source, props, object);
|
||||
}
|
||||
var assignWith = createAssigner(function(object, source, customizer) {
|
||||
copyObjectWith(source, keys(source), customizer, object);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -8706,7 +8732,7 @@
|
||||
*/
|
||||
var defaults = restParam(function(args) {
|
||||
args.push(undefined, extendDefaults);
|
||||
return extend.apply(undefined, args);
|
||||
return extendWith.apply(undefined, args);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -8729,7 +8755,7 @@
|
||||
*/
|
||||
var defaultsDeep = restParam(function(args) {
|
||||
args.push(undefined, mergeDefaults);
|
||||
return merge.apply(undefined, args);
|
||||
return mergeWith.apply(undefined, args);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -8741,20 +8767,38 @@
|
||||
* @category Object
|
||||
* @param {Object} object The destination object.
|
||||
* @param {...Object} [sources] The source objects.
|
||||
* @param {Function} [customizer] The function to customize assigned values.
|
||||
* @returns {Object} Returns `object`.
|
||||
* @example
|
||||
*
|
||||
* _.extend({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
|
||||
* // => { 'user': 'fred', 'age': 40 }
|
||||
*/
|
||||
var extend = createAssigner(function(object, source, customizer) {
|
||||
var props = keysIn(source);
|
||||
if (customizer) {
|
||||
copyObjectWith(source, props, customizer, object);
|
||||
} else {
|
||||
copyObject(source, props, object);
|
||||
}
|
||||
var extend = createAssigner(function(object, source) {
|
||||
copyObject(source, keysIn(source), object);
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is like `_.assignWith` except that it iterates over own and
|
||||
* inherited source properties.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Object
|
||||
* @param {Object} object The destination object.
|
||||
* @param {...Object} sources The source objects.
|
||||
* @param {Function} [customizer] The function to customize assigned values.
|
||||
* @returns {Object} Returns `object`.
|
||||
* @example
|
||||
*
|
||||
* var defaults = _.partialRight(_.extendWith, function(value, other) {
|
||||
* return _.isUndefined(value) ? other : value;
|
||||
* });
|
||||
*
|
||||
* defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
|
||||
* // => { 'user': 'barney', 'age': 36 }
|
||||
*/
|
||||
var extendWith = createAssigner(function(object, source, customizer) {
|
||||
copyObjectWith(source, keysIn(source), customizer, object);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -9265,6 +9309,71 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively merges own enumerable properties of the source object(s), that
|
||||
* don't resolve to `undefined` into the destination object. Subsequent sources
|
||||
* overwrite property assignments of previous sources.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Object
|
||||
* @param {Object} object The destination object.
|
||||
* @param {...Object} [sources] The source objects.
|
||||
* @returns {Object} Returns `object`.
|
||||
* @example
|
||||
*
|
||||
* var users = {
|
||||
* 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
|
||||
* };
|
||||
*
|
||||
* var ages = {
|
||||
* 'data': [{ 'age': 36 }, { 'age': 40 }]
|
||||
* };
|
||||
*
|
||||
* _.merge(users, ages);
|
||||
* // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
|
||||
*/
|
||||
var merge = createAssigner(function(object, source) {
|
||||
baseMerge(object, source);
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is like `_.merge` except that it accepts `customizer` which
|
||||
* is invoked to produce the merged values of the destination and source
|
||||
* properties. If `customizer` returns `undefined` merging is handled by the
|
||||
* method instead. The `customizer` is invoked with five arguments:
|
||||
* (objectValue, sourceValue, key, object, source).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Object
|
||||
* @param {Object} object The destination object.
|
||||
* @param {...Object} sources The source objects.
|
||||
* @param {Function} customizer The function to customize assigned values.
|
||||
* @returns {Object} Returns `object`.
|
||||
* @example
|
||||
*
|
||||
* var object = {
|
||||
* 'fruits': ['apple'],
|
||||
* 'vegetables': ['beet']
|
||||
* };
|
||||
*
|
||||
* var other = {
|
||||
* 'fruits': ['banana'],
|
||||
* 'vegetables': ['carrot']
|
||||
* };
|
||||
*
|
||||
* _.mergeWith(object, other, function(a, b) {
|
||||
* if (_.isArray(a)) {
|
||||
* return a.concat(b);
|
||||
* }
|
||||
* });
|
||||
* // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
|
||||
*/
|
||||
var mergeWith = createAssigner(function(object, source, customizer) {
|
||||
baseMerge(object, source, customizer);
|
||||
});
|
||||
|
||||
/**
|
||||
* The opposite of `_.pick`; this method creates an object composed of the
|
||||
* own and inherited enumerable properties of `object` that are not omitted.
|
||||
@@ -11335,6 +11444,7 @@
|
||||
lodash.after = after;
|
||||
lodash.ary = ary;
|
||||
lodash.assign = assign;
|
||||
lodash.assignWith = assignWith;
|
||||
lodash.at = at;
|
||||
lodash.before = before;
|
||||
lodash.bind = bind;
|
||||
@@ -11359,6 +11469,7 @@
|
||||
lodash.dropRightWhile = dropRightWhile;
|
||||
lodash.dropWhile = dropWhile;
|
||||
lodash.extend = extend;
|
||||
lodash.extendWith = extendWith;
|
||||
lodash.fill = fill;
|
||||
lodash.filter = filter;
|
||||
lodash.flatten = flatten;
|
||||
@@ -11382,6 +11493,7 @@
|
||||
lodash.matchesProperty = matchesProperty;
|
||||
lodash.memoize = memoize;
|
||||
lodash.merge = merge;
|
||||
lodash.mergeWith = mergeWith;
|
||||
lodash.method = method;
|
||||
lodash.methodOf = methodOf;
|
||||
lodash.mixin = mixin;
|
||||
@@ -11454,6 +11566,8 @@
|
||||
lodash.ceil = ceil;
|
||||
lodash.clone = clone;
|
||||
lodash.cloneDeep = cloneDeep;
|
||||
lodash.cloneDeepWith = cloneDeepWith;
|
||||
lodash.cloneWith = cloneWith;
|
||||
lodash.deburr = deburr;
|
||||
lodash.endsWith = endsWith;
|
||||
lodash.escape = escape;
|
||||
@@ -11488,10 +11602,12 @@
|
||||
lodash.isElement = isElement;
|
||||
lodash.isEmpty = isEmpty;
|
||||
lodash.isEqual = isEqual;
|
||||
lodash.isEqualWith = isEqualWith;
|
||||
lodash.isError = isError;
|
||||
lodash.isFinite = isFinite;
|
||||
lodash.isFunction = isFunction;
|
||||
lodash.isMatch = isMatch;
|
||||
lodash.isMatchWith = isMatchWith;
|
||||
lodash.isNaN = isNaN;
|
||||
lodash.isNative = isNative;
|
||||
lodash.isNull = isNull;
|
||||
|
||||
313
test/test.js
313
test/test.js
@@ -1039,27 +1039,35 @@
|
||||
var func = _[methodName];
|
||||
|
||||
test('`_.' + methodName + '` should assign properties of a source object to the destination object', 1, function() {
|
||||
deepEqual(_.assign({ 'a': 1 }, { 'b': 2 }), { 'a': 1, 'b': 2 });
|
||||
deepEqual(func({ 'a': 1 }, { 'b': 2 }), { 'a': 1, 'b': 2 });
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should accept multiple source objects', 2, function() {
|
||||
var expected = { 'a': 1, 'b': 2, 'c': 3 };
|
||||
deepEqual(_.assign({ 'a': 1 }, { 'b': 2 }, { 'c': 3 }), expected);
|
||||
deepEqual(_.assign({ 'a': 1 }, { 'b': 2, 'c': 2 }, { 'c': 3 }), expected);
|
||||
deepEqual(func({ 'a': 1 }, { 'b': 2 }, { 'c': 3 }), expected);
|
||||
deepEqual(func({ 'a': 1 }, { 'b': 2, 'c': 2 }, { 'c': 3 }), expected);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should overwrite destination properties', 1, function() {
|
||||
var expected = { 'a': 3, 'b': 2, 'c': 1 };
|
||||
deepEqual(_.assign({ 'a': 1, 'b': 2 }, expected), expected);
|
||||
deepEqual(func({ 'a': 1, 'b': 2 }, expected), expected);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should assign source properties with nullish values', 1, function() {
|
||||
var expected = { 'a': null, 'b': undefined, 'c': null };
|
||||
deepEqual(_.assign({ 'a': 1, 'b': 2 }, expected), expected);
|
||||
deepEqual(func({ 'a': 1, 'b': 2 }, expected), expected);
|
||||
});
|
||||
});
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.assignWith and lodash.extendWith');
|
||||
|
||||
_.each(['assignWith', 'extendWith'], function(methodName) {
|
||||
var func = _[methodName];
|
||||
|
||||
test('`_.' + methodName + '` should work with a `customizer` callback', 1, function() {
|
||||
var actual = _.assign({ 'a': 1, 'b': 2 }, { 'a': 3, 'c': 3 }, function(a, b) {
|
||||
var actual = func({ 'a': 1, 'b': 2 }, { 'a': 3, 'c': 3 }, function(a, b) {
|
||||
return typeof a == 'undefined' ? b : a;
|
||||
});
|
||||
|
||||
@@ -1068,7 +1076,7 @@
|
||||
|
||||
test('`_.' + methodName + '` should work with a `customizer` that returns `undefined`', 1, function() {
|
||||
var expected = { 'a': undefined };
|
||||
deepEqual(_.assign({}, expected, _.identity), expected);
|
||||
deepEqual(func({}, expected, _.identity), expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1963,23 +1971,6 @@
|
||||
var expected = typeof value == 'function' ? { 'c': Foo.c } : (value && {});
|
||||
deepEqual(func(value), expected);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should work with a `customizer` callback and ' + key, 4, function() {
|
||||
var customizer = function(value) {
|
||||
return _.isPlainObject(value) ? undefined : value;
|
||||
};
|
||||
|
||||
var actual = func(value, customizer);
|
||||
|
||||
deepEqual(actual, value);
|
||||
strictEqual(actual, value);
|
||||
|
||||
var object = { 'a': value, 'b': { 'c': value } };
|
||||
actual = func(object, customizer);
|
||||
|
||||
deepEqual(actual, object);
|
||||
notStrictEqual(actual, object);
|
||||
});
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should clone array buffers', 2, function() {
|
||||
@@ -2024,22 +2015,6 @@
|
||||
notStrictEqual(actual, shadowObject);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should provide the correct `customizer` arguments', 1, function() {
|
||||
var argsList = [],
|
||||
foo = new Foo;
|
||||
|
||||
func(foo, function() {
|
||||
argsList.push(slice.call(arguments));
|
||||
});
|
||||
|
||||
deepEqual(argsList, isDeep ? [[foo], [1, 'a', foo]] : [[foo]]);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should handle cloning if `customizer` returns `undefined`', 1, function() {
|
||||
var actual = func({ 'a': { 'b': 'c' } }, _.noop);
|
||||
deepEqual(actual, { 'a': { 'b': 'c' } });
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should clone `index` and `input` array properties', 2, function() {
|
||||
var array = /x/.exec('vwxyz'),
|
||||
actual = func(array);
|
||||
@@ -2122,6 +2097,46 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
_.each(['cloneWith', 'cloneDeepWith'], function(methodName) {
|
||||
var func = _[methodName],
|
||||
isDeepWith = methodName == 'cloneDeepWith';
|
||||
|
||||
test('`_.' + methodName + '` should provide the correct `customizer` arguments', 1, function() {
|
||||
var argsList = [],
|
||||
foo = new Foo;
|
||||
|
||||
func(foo, function() {
|
||||
argsList.push(slice.call(arguments));
|
||||
});
|
||||
|
||||
deepEqual(argsList, isDeepWith ? [[foo], [1, 'a', foo]] : [[foo]]);
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should handle cloning if `customizer` returns `undefined`', 1, function() {
|
||||
var actual = func({ 'a': { 'b': 'c' } }, _.noop);
|
||||
deepEqual(actual, { 'a': { 'b': 'c' } });
|
||||
});
|
||||
|
||||
_.forOwn(uncloneable, function(value, key) {
|
||||
test('`_.' + methodName + '` should work with a `customizer` callback and ' + key, 4, function() {
|
||||
var customizer = function(value) {
|
||||
return _.isPlainObject(value) ? undefined : value;
|
||||
};
|
||||
|
||||
var actual = func(value, customizer);
|
||||
|
||||
deepEqual(actual, value);
|
||||
strictEqual(actual, value);
|
||||
|
||||
var object = { 'a': value, 'b': { 'c': value } };
|
||||
actual = func(object, customizer);
|
||||
|
||||
deepEqual(actual, object);
|
||||
notStrictEqual(actual, object);
|
||||
});
|
||||
});
|
||||
});
|
||||
}(1, 2, 3));
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
@@ -5156,8 +5171,20 @@
|
||||
});
|
||||
|
||||
_.each(['assign', 'extend', 'merge'], function(methodName) {
|
||||
var func = _[methodName];
|
||||
|
||||
test('`_.' + methodName + '` should not treat `object` as `source`', 1, function() {
|
||||
function Foo() {}
|
||||
Foo.prototype.a = 1;
|
||||
|
||||
var actual = func(new Foo, { 'b': 2 });
|
||||
ok(!_.has(actual, 'a'));
|
||||
});
|
||||
});
|
||||
|
||||
_.each(['assignWith', 'extendWith', 'mergeWith'], function(methodName) {
|
||||
var func = _[methodName],
|
||||
isMerge = methodName == 'merge';
|
||||
isMergeWith = methodName == 'mergeWith';
|
||||
|
||||
test('`_.' + methodName + '` should provide the correct `customizer` arguments', 3, function() {
|
||||
var args,
|
||||
@@ -5192,20 +5219,12 @@
|
||||
});
|
||||
|
||||
var expected = [[objectValue, sourceValue, 'a', object, source]];
|
||||
if (isMerge) {
|
||||
if (isMergeWith) {
|
||||
expected.push([undefined, 2, 'b', sourceValue, sourceValue]);
|
||||
}
|
||||
deepEqual(argsList, expected, 'object property values');
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should not treat `object` as `source`', 1, function() {
|
||||
function Foo() {}
|
||||
Foo.prototype.a = 1;
|
||||
|
||||
var actual = func(new Foo, { 'b': 2 });
|
||||
ok(!_.has(actual, 'a'));
|
||||
});
|
||||
|
||||
test('`_.' + methodName + '` should not treat the second argument as a `customizer` callback', 2, function() {
|
||||
function callback() {}
|
||||
callback.b = 2;
|
||||
@@ -6968,82 +6987,6 @@
|
||||
deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('should provide the correct `customizer` arguments', 1, function() {
|
||||
var argsList = [],
|
||||
object1 = { 'a': [1, 2], 'b': null },
|
||||
object2 = { 'a': [1, 2], 'b': null };
|
||||
|
||||
object1.b = object2;
|
||||
object2.b = object1;
|
||||
|
||||
var expected = [
|
||||
[object1, object2],
|
||||
[object1.a, object2.a, 'a'],
|
||||
[object1.a[0], object2.a[0], 0],
|
||||
[object1.a[1], object2.a[1], 1],
|
||||
[object1.b, object2.b, 'b'],
|
||||
[object1.b.a, object2.b.a, 'a'],
|
||||
[object1.b.a[0], object2.b.a[0], 0],
|
||||
[object1.b.a[1], object2.b.a[1], 1],
|
||||
[object1.b.b, object2.b.b, 'b']
|
||||
];
|
||||
|
||||
_.isEqual(object1, object2, function() {
|
||||
argsList.push(slice.call(arguments));
|
||||
});
|
||||
|
||||
deepEqual(argsList, expected);
|
||||
});
|
||||
|
||||
test('should handle comparisons if `customizer` returns `undefined`', 3, function() {
|
||||
strictEqual(_.isEqual('a', 'a', _.noop), true);
|
||||
strictEqual(_.isEqual(['a'], ['a'], _.noop), true);
|
||||
strictEqual(_.isEqual({ '0': 'a' }, { '0': 'a' }, _.noop), true);
|
||||
});
|
||||
|
||||
test('should not handle comparisons if `customizer` returns `true`', 3, function() {
|
||||
var customizer = function(value) {
|
||||
return _.isString(value) || undefined;
|
||||
};
|
||||
|
||||
strictEqual(_.isEqual('a', 'b', customizer), true);
|
||||
strictEqual(_.isEqual(['a'], ['b'], customizer), true);
|
||||
strictEqual(_.isEqual({ '0': 'a' }, { '0': 'b' }, customizer), true);
|
||||
});
|
||||
|
||||
test('should not handle comparisons if `customizer` returns `false`', 3, function() {
|
||||
var customizer = function(value) {
|
||||
return _.isString(value) ? false : undefined;
|
||||
};
|
||||
|
||||
strictEqual(_.isEqual('a', 'a', customizer), false);
|
||||
strictEqual(_.isEqual(['a'], ['a'], customizer), false);
|
||||
strictEqual(_.isEqual({ '0': 'a' }, { '0': 'a' }, customizer), false);
|
||||
});
|
||||
|
||||
test('should return a boolean value even if `customizer` does not', 2, function() {
|
||||
var actual = _.isEqual('a', 'b', _.constant('c'));
|
||||
strictEqual(actual, true);
|
||||
|
||||
var values = _.without(falsey, undefined),
|
||||
expected = _.map(values, _.constant(false));
|
||||
|
||||
actual = [];
|
||||
_.each(values, function(value) {
|
||||
actual.push(_.isEqual('a', 'a', _.constant(value)));
|
||||
});
|
||||
|
||||
deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('should ensure `customizer` is a function', 1, function() {
|
||||
var array = [1, 2, 3],
|
||||
eq = _.partial(_.isEqual, array),
|
||||
actual = _.map([array, [1, 0, 3]], eq);
|
||||
|
||||
deepEqual(actual, [true, false]);
|
||||
});
|
||||
|
||||
test('should work as an iteratee for `_.every`', 1, function() {
|
||||
var actual = _.every([1, 1, 1], _.partial(_.isEqual, 1));
|
||||
ok(actual);
|
||||
@@ -7175,6 +7118,88 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.isEqualWith');
|
||||
|
||||
(function() {
|
||||
test('should provide the correct `customizer` arguments', 1, function() {
|
||||
var argsList = [],
|
||||
object1 = { 'a': [1, 2], 'b': null },
|
||||
object2 = { 'a': [1, 2], 'b': null };
|
||||
|
||||
object1.b = object2;
|
||||
object2.b = object1;
|
||||
|
||||
var expected = [
|
||||
[object1, object2],
|
||||
[object1.a, object2.a, 'a'],
|
||||
[object1.a[0], object2.a[0], 0],
|
||||
[object1.a[1], object2.a[1], 1],
|
||||
[object1.b, object2.b, 'b'],
|
||||
[object1.b.a, object2.b.a, 'a'],
|
||||
[object1.b.a[0], object2.b.a[0], 0],
|
||||
[object1.b.a[1], object2.b.a[1], 1],
|
||||
[object1.b.b, object2.b.b, 'b']
|
||||
];
|
||||
|
||||
_.isEqualWith(object1, object2, function() {
|
||||
argsList.push(slice.call(arguments));
|
||||
});
|
||||
|
||||
deepEqual(argsList, expected);
|
||||
});
|
||||
|
||||
test('should handle comparisons if `customizer` returns `undefined`', 3, function() {
|
||||
strictEqual(_.isEqualWith('a', 'a', _.noop), true);
|
||||
strictEqual(_.isEqualWith(['a'], ['a'], _.noop), true);
|
||||
strictEqual(_.isEqualWith({ '0': 'a' }, { '0': 'a' }, _.noop), true);
|
||||
});
|
||||
|
||||
test('should not handle comparisons if `customizer` returns `true`', 3, function() {
|
||||
var customizer = function(value) {
|
||||
return _.isString(value) || undefined;
|
||||
};
|
||||
|
||||
strictEqual(_.isEqualWith('a', 'b', customizer), true);
|
||||
strictEqual(_.isEqualWith(['a'], ['b'], customizer), true);
|
||||
strictEqual(_.isEqualWith({ '0': 'a' }, { '0': 'b' }, customizer), true);
|
||||
});
|
||||
|
||||
test('should not handle comparisons if `customizer` returns `false`', 3, function() {
|
||||
var customizer = function(value) {
|
||||
return _.isString(value) ? false : undefined;
|
||||
};
|
||||
|
||||
strictEqual(_.isEqualWith('a', 'a', customizer), false);
|
||||
strictEqual(_.isEqualWith(['a'], ['a'], customizer), false);
|
||||
strictEqual(_.isEqualWith({ '0': 'a' }, { '0': 'a' }, customizer), false);
|
||||
});
|
||||
|
||||
test('should return a boolean value even if `customizer` does not', 2, function() {
|
||||
var actual = _.isEqualWith('a', 'b', _.constant('c'));
|
||||
strictEqual(actual, true);
|
||||
|
||||
var values = _.without(falsey, undefined),
|
||||
expected = _.map(values, _.constant(false));
|
||||
|
||||
actual = [];
|
||||
_.each(values, function(value) {
|
||||
actual.push(_.isEqualWith('a', 'a', _.constant(value)));
|
||||
});
|
||||
|
||||
deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('should ensure `customizer` is a function', 1, function() {
|
||||
var array = [1, 2, 3],
|
||||
eq = _.partial(_.isEqualWith, array),
|
||||
actual = _.map([array, [1, 0, 3]], eq);
|
||||
|
||||
deepEqual(actual, [true, false]);
|
||||
});
|
||||
}());
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.isError');
|
||||
|
||||
(function() {
|
||||
@@ -7567,7 +7592,13 @@
|
||||
|
||||
deepEqual(actual, [false, true]);
|
||||
});
|
||||
}());
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.isMatchWith');
|
||||
|
||||
(function() {
|
||||
test('should provide the correct `customizer` arguments', 1, function() {
|
||||
var argsList = [],
|
||||
object1 = { 'a': [1, 2], 'b': null },
|
||||
@@ -7591,7 +7622,7 @@
|
||||
[object1.b.b.b, object2.b.b.b, 'b']
|
||||
];
|
||||
|
||||
_.isMatch(object1, object2, function() {
|
||||
_.isMatchWith(object1, object2, function() {
|
||||
argsList.push(slice.call(arguments));
|
||||
});
|
||||
|
||||
@@ -7599,12 +7630,12 @@
|
||||
});
|
||||
|
||||
test('should handle comparisons if `customizer` returns `undefined`', 1, function() {
|
||||
strictEqual(_.isMatch({ 'a': 1 }, { 'a': 1 }, _.noop), true);
|
||||
strictEqual(_.isMatchWith({ 'a': 1 }, { 'a': 1 }, _.noop), true);
|
||||
});
|
||||
|
||||
test('should return a boolean value even if `customizer` does not', 2, function() {
|
||||
var object = { 'a': 1 },
|
||||
actual = _.isMatch(object, { 'a': 1 }, _.constant('a'));
|
||||
actual = _.isMatchWith(object, { 'a': 1 }, _.constant('a'));
|
||||
|
||||
strictEqual(actual, true);
|
||||
|
||||
@@ -7612,7 +7643,7 @@
|
||||
|
||||
actual = [];
|
||||
_.each(falsey, function(value) {
|
||||
actual.push(_.isMatch(object, { 'a': 2 }, _.constant(value)));
|
||||
actual.push(_.isMatchWith(object, { 'a': 2 }, _.constant(value)));
|
||||
});
|
||||
|
||||
deepEqual(actual, expected);
|
||||
@@ -7620,7 +7651,7 @@
|
||||
|
||||
test('should ensure `customizer` is a function', 1, function() {
|
||||
var object = { 'a': 1 },
|
||||
matches = _.partial(_.isMatch, object),
|
||||
matches = _.partial(_.isMatchWith, object),
|
||||
actual = _.map([object, { 'a': 2 }], matches);
|
||||
|
||||
deepEqual(actual, [true, false]);
|
||||
@@ -10450,23 +10481,29 @@
|
||||
|
||||
deepEqual(actual, values);
|
||||
});
|
||||
}(1, 2, 3));
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash.mergeWith');
|
||||
|
||||
(function() {
|
||||
test('should handle merging if `customizer` returns `undefined`', 2, function() {
|
||||
var actual = _.merge({ 'a': { 'b': [1, 1] } }, { 'a': { 'b': [0] } }, _.noop);
|
||||
var actual = _.mergeWith({ 'a': { 'b': [1, 1] } }, { 'a': { 'b': [0] } }, _.noop);
|
||||
deepEqual(actual, { 'a': { 'b': [0, 1] } });
|
||||
|
||||
actual = _.merge([], [undefined], _.identity);
|
||||
actual = _.mergeWith([], [undefined], _.identity);
|
||||
deepEqual(actual, [undefined]);
|
||||
});
|
||||
|
||||
test('should defer to `customizer` when it returns a value other than `undefined`', 1, function() {
|
||||
var actual = _.merge({ 'a': { 'b': [0, 1] } }, { 'a': { 'b': [2] } }, function(a, b) {
|
||||
var actual = _.mergeWith({ 'a': { 'b': [0, 1] } }, { 'a': { 'b': [2] } }, function(a, b) {
|
||||
return _.isArray(a) ? a.concat(b) : undefined;
|
||||
});
|
||||
|
||||
deepEqual(actual, { 'a': { 'b': [0, 1, 2] } });
|
||||
});
|
||||
}(1, 2, 3));
|
||||
}());
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
@@ -11678,8 +11715,8 @@
|
||||
source = { 'a': { 'b': 2, 'c': 3 } },
|
||||
expected = { 'a': { 'b': 1, 'c': 3 } };
|
||||
|
||||
var defaultsDeep = _.partialRight(_.merge, function deep(value, other) {
|
||||
return _.isObject(value) ? _.merge(value, other, deep) : value;
|
||||
var defaultsDeep = _.partialRight(_.mergeWith, function deep(value, other) {
|
||||
return _.isObject(value) ? _.mergeWith(value, other, deep) : value;
|
||||
});
|
||||
|
||||
deepEqual(defaultsDeep(object, source), expected);
|
||||
@@ -17453,7 +17490,7 @@
|
||||
|
||||
var acceptFalsey = _.difference(allMethods, rejectFalsey);
|
||||
|
||||
test('should accept falsey arguments', 214, function() {
|
||||
test('should accept falsey arguments', 221, function() {
|
||||
var emptyArrays = _.map(falsey, _.constant([]));
|
||||
|
||||
_.each(acceptFalsey, function(methodName) {
|
||||
|
||||
Reference in New Issue
Block a user