UNPKG

froebel

Version:
73 lines (60 loc) 2.1 kB
import clone from "./clone.mjs"; const merge_ = (a, b) => { const visited = new Map(); return mergeItems(a, b, visited); }; const mergeItems = (a, b, visited) => { if (isPrimitive(a) || isPrimitive(b)) return clone(b); if (!visited.has(a)) visited.set(a, new Map()); if (visited.get(a).has(b)) return visited.get(a).get(b); let result; if (Array.isArray(b)) { const clonedA = Array.isArray(a) ? clone(a) : []; const clonedB = clone(b); result = [...clonedA, ...clonedB]; for (let i = 0; i < result.length; i++) { if (result[i] === clonedA || result[i] === clonedB) result[i] = result; } } else if (b instanceof Set) { result = new Set([...(a instanceof Set ? a : []), ...b]); if (result.has(a) || result.has(b)) { result.delete(a); result.delete(b); result.add(result); } } else if (b instanceof Map) { result = new Map([...(a instanceof Map ? a : []), ...b]); const keys = [...result.keys()]; if (keys.includes(a)) { result.set(result, result.get(a)); result.delete(a); } if (keys.includes(b)) { result.set(result, result.get(b)); result.delete(b); } for (const [k, v] of result) { if (v === a || v === b) result.set(k, result); } } else { const keys = [...new Set([...Object.keys(a), ...Object.keys(b)])]; result = {}; visited.get(a).set(b, result); for (const key of keys) { result[key] = !(key in b) ? clone(a[key]) : !(key in a) ? clone(b[key]) : mergeItems(a[key], b[key], visited); } } visited.get(a).set(b, result); return result; }; /** * Recursively merges `A` and `B`. If a property in `A` and `B` is of a * different type (i.e. it's not an array, Set, Map, or plain object in both, * the value from `B` will be used in the result). * * If there are self-references in the cloned values, array / Set items, or Map * keys or values, they will also be self-referencing in the result. */ const merge = merge_; export default merge; const isPrimitive = value => typeof value !== "object" || value === null;