UNPKG

8.53 kBJavaScriptView Raw
1/**
2 * lodash 3.3.2 (Custom Build) <https://lodash.com/>
3 * Build: `lodash modern modularize exports="npm" -o ./`
4 * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
5 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
6 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7 * Available under MIT license <https://lodash.com/license>
8 */
9var arrayCopy = require('lodash._arraycopy'),
10 arrayEach = require('lodash._arrayeach'),
11 createAssigner = require('lodash._createassigner'),
12 isArguments = require('lodash.isarguments'),
13 isArray = require('lodash.isarray'),
14 isPlainObject = require('lodash.isplainobject'),
15 isTypedArray = require('lodash.istypedarray'),
16 keys = require('lodash.keys'),
17 toPlainObject = require('lodash.toplainobject');
18
19/**
20 * Checks if `value` is object-like.
21 *
22 * @private
23 * @param {*} value The value to check.
24 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
25 */
26function isObjectLike(value) {
27 return !!value && typeof value == 'object';
28}
29
30/**
31 * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer)
32 * of an array-like value.
33 */
34var MAX_SAFE_INTEGER = 9007199254740991;
35
36/**
37 * The base implementation of `_.merge` without support for argument juggling,
38 * multiple sources, and `this` binding `customizer` functions.
39 *
40 * @private
41 * @param {Object} object The destination object.
42 * @param {Object} source The source object.
43 * @param {Function} [customizer] The function to customize merged values.
44 * @param {Array} [stackA=[]] Tracks traversed source objects.
45 * @param {Array} [stackB=[]] Associates values with source counterparts.
46 * @returns {Object} Returns `object`.
47 */
48function baseMerge(object, source, customizer, stackA, stackB) {
49 if (!isObject(object)) {
50 return object;
51 }
52 var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
53 props = isSrcArr ? undefined : keys(source);
54
55 arrayEach(props || source, function(srcValue, key) {
56 if (props) {
57 key = srcValue;
58 srcValue = source[key];
59 }
60 if (isObjectLike(srcValue)) {
61 stackA || (stackA = []);
62 stackB || (stackB = []);
63 baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
64 }
65 else {
66 var value = object[key],
67 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
68 isCommon = result === undefined;
69
70 if (isCommon) {
71 result = srcValue;
72 }
73 if ((result !== undefined || (isSrcArr && !(key in object))) &&
74 (isCommon || (result === result ? (result !== value) : (value === value)))) {
75 object[key] = result;
76 }
77 }
78 });
79 return object;
80}
81
82/**
83 * A specialized version of `baseMerge` for arrays and objects which performs
84 * deep merges and tracks traversed objects enabling objects with circular
85 * references to be merged.
86 *
87 * @private
88 * @param {Object} object The destination object.
89 * @param {Object} source The source object.
90 * @param {string} key The key of the value to merge.
91 * @param {Function} mergeFunc The function to merge values.
92 * @param {Function} [customizer] The function to customize merged values.
93 * @param {Array} [stackA=[]] Tracks traversed source objects.
94 * @param {Array} [stackB=[]] Associates values with source counterparts.
95 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
96 */
97function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
98 var length = stackA.length,
99 srcValue = source[key];
100
101 while (length--) {
102 if (stackA[length] == srcValue) {
103 object[key] = stackB[length];
104 return;
105 }
106 }
107 var value = object[key],
108 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
109 isCommon = result === undefined;
110
111 if (isCommon) {
112 result = srcValue;
113 if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
114 result = isArray(value)
115 ? value
116 : (isArrayLike(value) ? arrayCopy(value) : []);
117 }
118 else if (isPlainObject(srcValue) || isArguments(srcValue)) {
119 result = isArguments(value)
120 ? toPlainObject(value)
121 : (isPlainObject(value) ? value : {});
122 }
123 else {
124 isCommon = false;
125 }
126 }
127 // Add the source value to the stack of traversed objects and associate
128 // it with its merged value.
129 stackA.push(srcValue);
130 stackB.push(result);
131
132 if (isCommon) {
133 // Recursively merge objects and arrays (susceptible to call stack limits).
134 object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
135 } else if (result === result ? (result !== value) : (value === value)) {
136 object[key] = result;
137 }
138}
139
140/**
141 * The base implementation of `_.property` without support for deep paths.
142 *
143 * @private
144 * @param {string} key The key of the property to get.
145 * @returns {Function} Returns the new function.
146 */
147function baseProperty(key) {
148 return function(object) {
149 return object == null ? undefined : object[key];
150 };
151}
152
153/**
154 * Gets the "length" property value of `object`.
155 *
156 * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
157 * that affects Safari on at least iOS 8.1-8.3 ARM64.
158 *
159 * @private
160 * @param {Object} object The object to query.
161 * @returns {*} Returns the "length" value.
162 */
163var getLength = baseProperty('length');
164
165/**
166 * Checks if `value` is array-like.
167 *
168 * @private
169 * @param {*} value The value to check.
170 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
171 */
172function isArrayLike(value) {
173 return value != null && isLength(getLength(value));
174}
175
176/**
177 * Checks if `value` is a valid array-like length.
178 *
179 * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
180 *
181 * @private
182 * @param {*} value The value to check.
183 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
184 */
185function isLength(value) {
186 return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
187}
188
189/**
190 * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
191 * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
192 *
193 * @static
194 * @memberOf _
195 * @category Lang
196 * @param {*} value The value to check.
197 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
198 * @example
199 *
200 * _.isObject({});
201 * // => true
202 *
203 * _.isObject([1, 2, 3]);
204 * // => true
205 *
206 * _.isObject(1);
207 * // => false
208 */
209function isObject(value) {
210 // Avoid a V8 JIT bug in Chrome 19-20.
211 // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
212 var type = typeof value;
213 return !!value && (type == 'object' || type == 'function');
214}
215
216/**
217 * Recursively merges own enumerable properties of the source object(s), that
218 * don't resolve to `undefined` into the destination object. Subsequent sources
219 * overwrite property assignments of previous sources. If `customizer` is
220 * provided it is invoked to produce the merged values of the destination and
221 * source properties. If `customizer` returns `undefined` merging is handled
222 * by the method instead. The `customizer` is bound to `thisArg` and invoked
223 * with five arguments: (objectValue, sourceValue, key, object, source).
224 *
225 * @static
226 * @memberOf _
227 * @category Object
228 * @param {Object} object The destination object.
229 * @param {...Object} [sources] The source objects.
230 * @param {Function} [customizer] The function to customize assigned values.
231 * @param {*} [thisArg] The `this` binding of `customizer`.
232 * @returns {Object} Returns `object`.
233 * @example
234 *
235 * var users = {
236 * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
237 * };
238 *
239 * var ages = {
240 * 'data': [{ 'age': 36 }, { 'age': 40 }]
241 * };
242 *
243 * _.merge(users, ages);
244 * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
245 *
246 * // using a customizer callback
247 * var object = {
248 * 'fruits': ['apple'],
249 * 'vegetables': ['beet']
250 * };
251 *
252 * var other = {
253 * 'fruits': ['banana'],
254 * 'vegetables': ['carrot']
255 * };
256 *
257 * _.merge(object, other, function(a, b) {
258 * if (_.isArray(a)) {
259 * return a.concat(b);
260 * }
261 * });
262 * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
263 */
264var merge = createAssigner(baseMerge);
265
266module.exports = merge;