Overview
The DiffUtils class provides utilities for deep comparison, change detection, and patch generation. Perfect for tracking object changes, implementing undo/redo, synchronizing data, and detecting differences in complex data structures.
Import
import { DiffUtils } from 'bytekit';
Usage
Deep Comparison
const obj1 = { name: 'John', age: 30, meta: { role: 'admin' } };
const obj2 = { name: 'John', age: 30, meta: { role: 'admin' } };
const obj3 = { name: 'John', age: 31, meta: { role: 'admin' } };
DiffUtils.deepEqual(obj1, obj2); // true
DiffUtils.deepEqual(obj1, obj3); // false
Detect Changes
const oldState = {
name: 'John',
age: 30,
role: 'user'
};
const newState = {
name: 'John',
age: 31,
role: 'admin',
verified: true
};
const diff = DiffUtils.diff(oldState, newState);
console.log(diff);
// {
// changed: ['age', 'role'],
// added: ['verified'],
// removed: []
// }
Generate and Apply Patches
const original = { count: 0, status: 'idle' };
const updated = { count: 5, status: 'active', timestamp: 1234567890 };
// Generate patches
const patches = DiffUtils.createPatch(original, updated);
console.log(patches);
// [
// { op: 'replace', path: 'count', value: 5, oldValue: 0 },
// { op: 'replace', path: 'status', value: 'active', oldValue: 'idle' },
// { op: 'add', path: 'timestamp', value: 1234567890 }
// ]
// Apply patches
const result = DiffUtils.applyPatch(original, patches);
console.log(result);
// { count: 5, status: 'active', timestamp: 1234567890 }
Undo/Redo Implementation
const original = { count: 0 };
const updated = { count: 5 };
// Create forward patches
const patches = DiffUtils.createPatch(original, updated);
// Apply changes
let state = DiffUtils.applyPatch(original, patches);
// Undo - reverse patches
const undoPatches = DiffUtils.reversePatch(patches);
state = DiffUtils.applyPatch(state, undoPatches);
console.log(state); // { count: 0 }
Deep Diff with Nested Paths
const oldObj = {
user: {
profile: { name: 'John', age: 30 },
settings: { theme: 'dark' }
}
};
const newObj = {
user: {
profile: { name: 'John', age: 31 },
settings: { theme: 'light', notifications: true }
}
};
const deepDiff = DiffUtils.deepDiff(oldObj, newObj);
console.log(deepDiff);
// {
// changed: ['user.profile.age', 'user.settings.theme'],
// added: ['user.settings.notifications'],
// removed: []
// }
Merge Objects
const obj1 = { name: 'John', age: 30, meta: { role: 'user' } };
const obj2 = { age: 31, meta: { verified: true }, email: 'john@example.com' };
// Deep merge
const merged = DiffUtils.merge(obj1, obj2);
console.log(merged);
// {
// name: 'John',
// age: 31,
// meta: { role: 'user', verified: true },
// email: 'john@example.com'
// }
// Use first object's values
const first = DiffUtils.merge(obj1, obj2, 'first');
// Use second object's values
const second = DiffUtils.merge(obj1, obj2, 'second');
API Reference
deepEqual
Deep compare two values for equality.
Returns true if values are deeply equal
DiffUtils.deepEqual({ a: 1 }, { a: 1 }); // true
DiffUtils.deepEqual({ a: 1 }, { a: 2 }); // false
diff
Compare two objects and return changed, added, and removed keys.
oldObj
Record<string, unknown>
required
Original object
newObj
Record<string, unknown>
required
New object to compare
Object containing arrays of changed, added, and removed keysinterface DiffResult {
changed: string[];
added: string[];
removed: string[];
}
deepDiff
Perform deep diff with nested paths using dot notation.
Path prefix for nested keys
Object with nested paths (e.g., ‘user.profile.age’)
createPatch
Generate patches to transform one object into another.
oldObj
Record<string, unknown>
required
Original object
newObj
Record<string, unknown>
required
Target object
Array of patch operationsinterface Patch {
op: 'add' | 'remove' | 'replace';
path: string;
value?: unknown;
oldValue?: unknown;
}
applyPatch
Apply patches to an object.
obj
Record<string, unknown>
required
Object to patch
Array of patches to apply
New object with patches applied (does not mutate original)
reversePatch
Reverse patches to create undo operations.
Reversed patches for undo
const patches = DiffUtils.createPatch(oldObj, newObj);
const undoPatches = DiffUtils.reversePatch(patches);
merge
Merge two objects with conflict resolution strategy.
obj1
Record<string, unknown>
required
First object
obj2
Record<string, unknown>
required
Second object
strategy
'first' | 'second' | 'merge'
default:"'merge'"
Conflict resolution strategy:
'first': Use values from obj1
'second': Use values from obj2
'merge': Deep merge (default)
getSummary
Get a human-readable summary of changes.
Diff result from diff() or deepDiff()
Summary string (e.g., “2 changed, 1 added, 0 removed”)
const diff = DiffUtils.diff(oldObj, newObj);
const summary = DiffUtils.getSummary(diff);
console.log(summary); // "2 changed, 1 added"
Types
DiffResult
interface DiffResult {
changed: string[];
added: string[];
removed: string[];
}
Patch
interface Patch {
op: 'add' | 'remove' | 'replace';
path: string;
value?: unknown;
oldValue?: unknown;
}
Use Cases
State Management
class StateManager {
private history: Patch[][] = [];
private state: any;
update(newState: any) {
const patches = DiffUtils.createPatch(this.state, newState);
this.history.push(patches);
this.state = newState;
}
undo() {
const patches = this.history.pop();
if (patches) {
const undoPatches = DiffUtils.reversePatch(patches);
this.state = DiffUtils.applyPatch(this.state, undoPatches);
}
}
}
Data Sync
const localData = await fetchLocal();
const serverData = await fetchServer();
const diff = DiffUtils.diff(localData, serverData);
if (diff.changed.length > 0 || diff.added.length > 0) {
const patches = DiffUtils.createPatch(localData, serverData);
await syncToServer(patches);
}
Change Tracking
const observer = {
original: data,
check(current: any) {
const diff = DiffUtils.deepDiff(this.original, current);
const summary = DiffUtils.getSummary(diff);
if (diff.changed.length > 0) {
console.log(`Changes detected: ${summary}`);
console.log('Modified fields:', diff.changed);
}
}
};