shelving
Version:
Toolkit for using data in JavaScript.
56 lines (55 loc) • 1.98 kB
JavaScript
import { isArray } from "./array.js";
import { isArrayEqual, isDeepEqual } from "./equal.js";
import { isObject } from "./object.js";
/** The `SAME` symbol indicates sameness. */
export const SAME = Symbol("shelving/SAME");
export function deepDiff(left, right) {
if (left === right)
return SAME;
if (isArray(right))
return isArray(left) ? deepDiffArray(left, right) : right;
if (isObject(right))
return isObject(left) && !isArray(left) ? deepDiffObject(left, right) : right;
return right;
}
/**
* Diff two arrays to produce the transformation needed to transform `left` into `right`
* DH: Currently arrays don't diff at an item level, they return the entire new array if not deeply equal.
*
* @returns The `right` array if it is different to `left`, or the exact `SAME` constant otherwise.
* - If the two values are deeply equal the `SAME` constant is returned.
*/
export function deepDiffArray(left, right) {
if (left === right)
return SAME;
if (isArrayEqual(left, right, isDeepEqual))
return SAME; // Left is exactly equal to right so return `SAME`
return right; // Right must be different to left so return right.
}
export function deepDiffObject(left, right) {
if (left === right)
return SAME;
const rightKeys = Object.keys(right);
const leftKeys = Object.keys(left);
// If left is empty, entire right is returned.
if (!leftKeys.length)
return rightKeys.length ? right : SAME;
const diff = {};
let changed = false;
for (const k of rightKeys) {
const d = deepDiff(left[k], right[k]);
if (d !== SAME) {
diff[k] = d;
changed = true;
}
}
// Loop through left to find any deleted docs.
for (const k of leftKeys) {
if (!rightKeys.includes(k)) {
diff[k] = undefined;
changed = true;
}
}
// If any properties changed.
return changed ? diff : SAME;
}