Add _.merge.

Former-commit-id: e393655b1fa41c8eb6ae1b925f456aa05231078a
This commit is contained in:
John-David Dalton
2012-08-02 00:28:19 -07:00
parent 896b8f7cf1
commit 5a9a18501d
3 changed files with 119 additions and 55 deletions

View File

@@ -204,6 +204,7 @@
'map': ['identity'],
'max': [],
'memoize': [],
'merge': ['isArray'],
'min': [],
'mixin': ['forEach', 'functions'],
'noConflict': [],
@@ -270,6 +271,7 @@
'drop',
'forIn',
'forOwn',
'merge',
'partial',
'where',
'zipObject'
@@ -838,11 +840,11 @@
modified = snippet;
// remove native `Function#bind` branch in `_.bind`
if (funcName == 'bind' ) {
if (funcName == 'bind') {
modified = modified.replace(/(?:\s*\/\/.*)*\s*else if *\(isBindFast[^}]+}/, '');
}
// remove native `Array.isArray` branch in `_.isArray`
else if (funcName == 'isArray') {
else {
modified = modified.replace(/nativeIsArray * \|\|/, '');
}
source = source.replace(snippet, modified);
@@ -907,8 +909,8 @@
// remove `noNodeClass` assignment
source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *try *\{(?:\s*\/\/.*)*\n *var noNodeClass[\s\S]+?catch[^}]+}\n/, '');
// remove `noNodeClass` from `_.clone`
source = source.replace(/(?:\s*\/\/.*)*\n *isObj *= *!noNodeClass.+\n/, '');
// remove `noNodeClass` from `isPlainObject`
source = source.replace(/\(!noNodeClass *\|\|[\s\S]+?\)\) *&&/, '');
// remove `noNodeClass` from `_.isEqual`
source = source.replace(/ *\|\| *\(noNodeClass *&&[\s\S]+?\)\)\)/, '');

View File

@@ -9,15 +9,16 @@
var compiledVars = [
'accumulator',
'args',
'arrayClass',
'arrayLikeClasses',
'ArrayProto',
'bind',
'callback',
'callee',
'collection',
'compareAscending',
'concat',
'ctor',
'destValue',
'forIn',
'funcClass',
'funcs',
@@ -25,8 +26,11 @@
'identity',
'index',
'indexOf',
'isArr',
'isArray',
'isArguments',
'isFunc',
'isPlainObject',
'iteratee',
'iterateeIndex',
'iteratorBind',
@@ -81,6 +85,9 @@
/** Used to minify variables and string values to a single character */
var minNames = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
minNames.push.apply(minNames, minNames.map(function(value) {
return value + value;
}));
/** Used to protect the specified properties from getting minified */
var propWhitelist = [
@@ -162,6 +169,7 @@
'map',
'max',
'memoize',
'merge',
'methods',
'min',
'mixin',

154
lodash.js
View File

@@ -250,8 +250,9 @@
}
/**
* By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters,
* change the following template settings to use alternative delimiters.
* By default, the template delimiters used by Lo-Dash are similar to those in
* embedded Ruby (ERB). Change the following template settings to use alternative
* delimiters.
*
* @static
* @memberOf _
@@ -631,16 +632,19 @@
}
// create the function factory
var factory = Function(
'arrayClass, arrayLikeClasses, ArrayProto, bind, compareAscending, concat, ' +
'forIn, funcClass, hasOwnProperty, identity, indexOf, isArguments, iteratorBind, ' +
'objectTypes, nativeKeys, propertyIsEnumerable, slice, stringClass, toString',
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, ' +
'funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray, ' +
'isPlainObject, iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' +
'slice, stringClass, toString',
'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' +
'return callee'
);
// return the compiled function
return factory(
arrayClass, arrayLikeClasses, ArrayProto, bind, compareAscending, concat,
forIn, funcClass, hasOwnProperty, identity, indexOf, isArguments, iteratorBind,
objectTypes, nativeKeys, propertyIsEnumerable, slice, stringClass, toString
arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn,
funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray,
isPlainObject, iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable,
slice, stringClass, toString
);
}
@@ -701,6 +705,33 @@
return htmlEscapes[match];
}
/**
* Checks if `value` is a plain `Object` object with `Object` as its constructor.
*
* @private
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, else `false`.
*/
function isPlainObject(value) {
var result = false;
if (!(value && typeof value == 'object')) {
return result;
}
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
// methods that are `typeof` "string" and still can coerce nodes to strings.
// Also check that the constructor is `Object` (i.e. `Object instanceof Object`)
var ctor = value.constructor;
if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) &&
(toString.call(ctor) != funcClass || ctor instanceof ctor)) {
// An object's own properties are iterated before inherited properties.
// If the last iterated key belongs to an object's own property then
// there are no inherited enumerable properties.
forIn(value, function(objValue, objKey) { result = objKey; });
result = result === false || hasOwnProperty.call(value, result);
}
return result;
}
/**
* Creates a new function that, when called, invokes `func` with the `this`
* binding of `thisArg` and the arguments (value, index, object).
@@ -863,29 +894,8 @@
if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) {
return value;
}
var useCtor,
ctor = value.constructor,
isArr = className == arrayClass;
if (className == objectClass) {
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
// methods that are `typeof` "string" and still can coerce nodes to strings
isObj = !noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string');
if (isObj) {
// check that the constructor is `Object` because `Object instanceof Object` is `true`
useCtor = toString.call(ctor) == funcClass;
isObj = !useCtor || ctor instanceof ctor;
}
if (isObj) {
// An object's own properties are iterated before inherited properties.
// If the last iterated key belongs to an object's own property then
// there are no inherited enumerable properties.
forIn(value, function(objValue, objKey) { isObj = objKey; });
isObj = isObj == true || hasOwnProperty.call(value, isObj);
}
}
var isArr = className == arrayClass;
isObj = isArr || (className == objectClass ? isPlainObject(value) : isObj);
}
// shallow clone
if (!isObj || !deep) {
@@ -895,6 +905,7 @@
: value;
}
var ctor = value.constructor;
switch (className) {
case boolClass:
return new ctor(value == true);
@@ -920,7 +931,7 @@
// init cloned object
length = value.length;
var result = isArr ? ctor(length) : (useCtor ? new ctor : {});
var result = isArr ? ctor(length) : {};
// add current clone and original value to the stack of traversed objects
stack.push({ 'clone': result, 'value': value });
@@ -950,7 +961,7 @@
* @category Objects
* @param {Object} object The destination object.
* @param {Object} [default1, default2, ...] The default objects.
* @returns {Object} Returns the `object`.
* @returns {Object} Returns the destination object.
* @example
*
* var iceCream = { 'flavor': 'chocolate' };
@@ -995,7 +1006,7 @@
* @category Objects
* @param {Object} object The destination object.
* @param {Object} [source1, source2, ...] The source objects.
* @returns {Object} Returns the `object`.
* @returns {Object} Returns the destination object.
* @example
*
* _.extend({ 'name': 'moe' }, { 'age': 40 });
@@ -1014,7 +1025,7 @@
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding for the callback.
* @returns {Object} Returns the `object`.
* @returns {Object} Returns `object`.
* @example
*
* function Dog(name) {
@@ -1045,7 +1056,7 @@
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding for the callback.
* @returns {Object} Returns the `object`.
* @returns {Object} Returns `object`.
* @example
*
* _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
@@ -1829,7 +1840,7 @@
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding for the callback.
* @returns {Array|Object} Returns the `collection`.
* @returns {Array|Object} Returns `collection`.
* @example
*
* _([1, 2, 3]).forEach(alert).join(',');
@@ -1932,6 +1943,48 @@
*/
var map = createIterator(baseIteratorOptions, mapIteratorOptions);
/**
* Merges enumerable properties of the source object(s) into the `destination`
* object. Subsequent sources will overwrite propery assignments of previous
* sources.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
* @param {Object} [source1, source2, ...] The source objects.
* @returns {Object} Returns the destination object.
* @example
*
* var stooges = [
* { 'name': 'moe' },
* { 'name': 'larry' }
* ];
*
* var ages = [
* { 'age': 40 },
* { 'age': 50 }
* ];
*
* _.merge(stooges, ages);
* // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]
*/
var merge = createIterator(extendIteratorOptions, {
'top': 'var destValue, isArr;\n' + extendIteratorOptions.top,
'inLoop':
'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' +
' destValue = result[index];\n' +
' if (isArr) {\n' +
' destValue = destValue && isArray(destValue) ? destValue : []\n' +
' } else {\n' +
' destValue = destValue && isPlainObject(destValue) ? destValue : {}\n' +
' }\n' +
' result[index] = callee(destValue, value)\n' +
'} else if (value != null) {\n' +
' result[index] = value\n' +
'}'
});
/**
* Retrieves the value of a specified property from all elements in
* the `collection`.
@@ -2907,7 +2960,7 @@
}
/**
* Merges the elements of each array at their corresponding indexes. Useful for
* Groups the elements of each array at their corresponding indexes. Useful for
* separate data sources that are coordinated through matching array indexes.
* For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix
* in a similar fashion.
@@ -2916,7 +2969,7 @@
* @memberOf _
* @category Arrays
* @param {Array} [array1, array2, ...] Arrays to process.
* @returns {Array} Returns a new array of merged arrays.
* @returns {Array} Returns a new array of grouped elements.
* @example
*
* _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
@@ -2937,7 +2990,7 @@
}
/**
* Merges an array of `keys` and an array of `values` into a single object.
* Creates an object composed from an array of `keys` and an array of `values`.
*
* @static
* @memberOf _
@@ -3099,7 +3152,7 @@
* @category Functions
* @param {Object} object The object to bind and assign the bound methods to.
* @param {String} [methodName1, methodName2, ...] Method names on the object to bind.
* @returns {Object} Returns the `object`.
* @returns {Object} Returns `object`.
* @example
*
* var buttonView = {
@@ -3436,8 +3489,8 @@
* @returns {String} Returns the escaped string.
* @example
*
* _.escape('Curly, Larry & Moe');
* // => "Curly, Larry &amp; Moe"
* _.escape('Moe, Larry & Curly');
* // => "Moe, Larry &amp; Curly"
*/
function escape(string) {
return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar);
@@ -3478,11 +3531,11 @@
* }
* });
*
* _.capitalize('curly');
* // => 'Curly'
*
* _('larry').capitalize();
* _.capitalize('larry');
* // => 'Larry'
*
* _('curly').capitalize();
* // => 'Curly'
*/
function mixin(object) {
forEach(functions(object), function(methodName) {
@@ -3577,8 +3630,8 @@
* // => 'hello: moe'
*
* var list = '<% _.forEach(people, function(name) { %> <li><%= name %></li> <% }); %>';
* _.template(list, { 'people': ['moe', 'curly', 'larry'] });
* // => '<li>moe</li><li>curly</li><li>larry</li>'
* _.template(list, { 'people': ['moe', 'larry', 'curly'] });
* // => '<li>moe</li><li>larry</li><li>curly</li>'
*
* var template = _.template('<b><%- value %></b>');
* template({ 'value': '<script>' });
@@ -3937,6 +3990,7 @@
lodash.map = map;
lodash.max = max;
lodash.memoize = memoize;
lodash.merge = merge;
lodash.min = min;
lodash.mixin = mixin;
lodash.noConflict = noConflict;