Skip to content

Commit

Permalink
Core: mild mergedeep speedup (#12718)
Browse files Browse the repository at this point in the history
* Core: mergedeep speedup

uses direct assignment, loops over top level

* Update utils.js

* Update utils.js

* linting
  • Loading branch information
patmmccann authored Feb 13, 2025
1 parent 34f6070 commit b4253d9
Showing 1 changed file with 42 additions and 29 deletions.
71 changes: 42 additions & 29 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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++) {
Expand Down

0 comments on commit b4253d9

Please sign in to comment.