From b4253d9f677de640660431fd468a08ef9d538aa9 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Feb 2025 10:38:12 -0500 Subject: [PATCH] Core: mild mergedeep speedup (#12718) * Core: mergedeep speedup uses direct assignment, loops over top level * Update utils.js * Update utils.js * linting --- src/utils.js | 71 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/src/utils.js b/src/utils.js index 015b1142d47..a412028b4b6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1023,38 +1023,51 @@ export function deepEqual(obj1, obj2, {checkTypes = false} = {}) { } export function mergeDeep(target, ...sources) { - if (!sources.length) return target; - const source = sources.shift(); - - if (isPlainObject(target) && isPlainObject(source)) { - for (const key in source) { - if (isPlainObject(source[key])) { - if (!target[key]) Object.assign(target, { [key]: {} }); - mergeDeep(target[key], source[key]); - } else if (isArray(source[key])) { - if (!target[key]) { - Object.assign(target, { [key]: [...source[key]] }); - } else if (isArray(target[key])) { - source[key].forEach(obj => { - let addItFlag = 1; - for (let i = 0; i < target[key].length; i++) { - if (deepEqual(target[key][i], obj)) { - addItFlag = 0; - break; - } - } - if (addItFlag) { - target[key].push(obj); - } - }); - } + for (let i = 0; i < sources.length; i++) { + const source = sources[i]; + if (!isPlainObject(source)) { + continue; + } + mergeDeepHelper(target, source); + } + return target; +} + +function mergeDeepHelper(target, source) { + // quick check + if (!isPlainObject(target) || !isPlainObject(source)) { + return; + } + + const keys = Object.keys(source); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (key === '__proto__' || key === 'constructor') { + continue; + } + const val = source[key]; + + if (isPlainObject(val)) { + if (!target[key]) { + target[key] = {}; + } + mergeDeepHelper(target[key], val); + } else if (Array.isArray(val)) { + if (!Array.isArray(target[key])) { + target[key] = [...val]; } else { - Object.assign(target, { [key]: source[key] }); + // deduplicate + val.forEach(obj => { + if (!target[key].some(item => deepEqual(item, obj))) { + target[key].push(obj); + } + }); } + } else { + // direct assignment + target[key] = val; } } - - return mergeDeep(target, ...sources); } /** @@ -1245,7 +1258,7 @@ export function hasNonSerializableProperty(obj, checkedObjects = new Set()) { * * @param {Array} collection - Array of objects. * @param {String} key - Key of nested property. - * @returns {any, undefined} - Value of nested property. + * @returns {any|undefined} - Value of nested property. */ export function setOnAny(collection, key) { for (let i = 0, result; i < collection.length; i++) {