shelving
Version:
Toolkit for using data in JavaScript.
67 lines (66 loc) • 2 kB
JavaScript
import { isArray, withArrayItems } from "./array.js";
import { isObject } from "./object.js";
// Internal shared by shallow/deep merge.
function _merge(left, right, recursor) {
if (left === right)
return right;
if (isArray(right))
return isArray(left) ? mergeArray(left, right) : right;
if (isObject(right))
return isObject(left) && !isArray(left) ? mergeObject(left, right, recursor) : right;
return right;
}
/**
* Exact merge two unknown values.
* - Always returns `right`.
*/
export function exactMerge(left, right) {
return right;
}
export function shallowMerge(left, right) {
return _merge(left, right, exactMerge);
}
export function deepMerge(left, right) {
return _merge(left, right, deepMerge);
}
export function mergeArray(left, right) {
if (left === right)
return right;
if (!right.length)
return left;
if (!left.length)
return right;
return withArrayItems(left, ...right);
}
export function mergeObject(left, right, recursor = exactMerge) {
if (left === right)
return right;
// If `right` has no keys then merge result will always be `left` (because there's nothing to merge).
const rightEntries = Object.entries(right);
if (!rightEntries.length)
return left;
const leftKeys = Object.keys(left);
const merged = { ...left };
let changed = false;
for (const [k, r] of rightEntries) {
if (leftKeys.includes(k)) {
if (r === undefined) {
changed = true; // Literal `undefined` means "delete this prop"
}
else {
const l = left[k];
const m = recursor(l, r);
if (m !== l) {
changed = true;
merged[k] = m;
}
}
}
else {
changed = true;
if (r !== undefined)
merged[k] = r;
}
}
return changed ? merged : left;
}