current when next is deeply equal to it; otherwise a value
deeply equal to next that reuses the equal parts of current
type PullReport = DeepReadonly<{
summary: { open: number };
pulls: { number: number; title: string }[];
}>;
const held: PullReport = {
summary: { open: 2 },
pulls: [
{ number: 1, title: 'one' },
{ number: 2, title: 'two' },
],
} as const;
// A server answers with the whole report every time.
const answer: PullReport = {
summary: { open: 2 },
pulls: [
{ number: 1, title: 'one' },
{ number: 2, title: 'two (edited)' },
],
} as const;
const written = replaceEqualDeep(held, answer);
assert.deepStrictEqual(written, answer);
// What did not change is the object already held,
assert.strictEqual(written.summary, held.summary);
assert.strictEqual(written.pulls[0], held.pulls[0]);
// and what changed is new.
assert.notStrictEqual(written.pulls[1], held.pulls[1]);
// An answer that changes nothing gives back the value held.
assert.strictEqual(replaceEqualDeep(held, structuredClone(held)), held);
// Match array items by key rather than by index.
const opened = replaceEqualDeep(
held.pulls,
[{ number: 0, title: 'zero' }, ...structuredClone(held.pulls)],
{
key: (item) =>
isRecord(item) && hasKey(item, 'number') ? item.number : undefined,
},
);
assert.strictEqual(opened[1], held.pulls[0]);
assert.strictEqual(opened[2], held.pulls[1]);
Returns
nextwith every part deeply equal to the same part ofcurrentreplaced by that part ofcurrent(structural sharing). When the whole ofnextis deeply equal tocurrent,currentitself is returned, so anObject.iscomparison of the result withcurrenttells whether anything changed, and a reader that compares by reference sees a new object only where the value really changed.Only plain objects and arrays are walked into. Anything else — a
Map, aSet, aDate, a class instance — is compared whole with fastDeepEqual and kept or replaced whole.Array items are matched by index. Give
keyto match them by key instead: an item for which it returns something other thanundefinedis matched with the item ofcurrentit returns the same key for, and an item it returnsundefinedfor is still matched by index. The onekeyis used for the arrays at every depth.Neither argument is changed. The result shares objects with both, so it relies on neither being changed afterwards.
Call it where the new value is written over the one held, so that nothing downstream has a new object it must compare again.