UNPKG

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