objectDiff()
Find differences between two objects with optional depth control and detailed change tracking.
Utility Function
Objects
Comparison
Syntax
objectDiff(obj1, obj2, options)Parameters
obj1 Object
The first object to compare (original).
obj2 Object
The second object to compare (modified).
options object optional
Configuration options.
depth- Maximum depth to compare (default: Infinity)includeEqual- Include unchanged values (default: false)arrayDiff- How to handle array differences (default: 'replace')ignoreKeys- Array of keys to ignore during comparison
Returns
Object
- Object containing the differences with change metadata.Examples
Basic Object Comparison
const original = {
name: "John",
age: 30,
city: "New York"
};
const modified = {
name: "John",
age: 31,
city: "Boston"
};
const diff = objectDiff(original, modified);
console.log(diff);
// {
// age: { from: 30, to: 31 },
// city: { from: "New York", to: "Boston" }
// }Nested Object Differences
const obj1 = {
user: {
profile: {
name: "Alice",
settings: {
theme: "dark",
notifications: true
}
},
posts: 5
}
};
const obj2 = {
user: {
profile: {
name: "Alice",
settings: {
theme: "light",
notifications: true,
language: "en"
}
},
posts: 7
}
};
const diff = objectDiff(obj1, obj2);
console.log(diff);
// {
// user: {
// profile: {
// settings: {
// theme: { from: "dark", to: "light" },
// language: { from: undefined, to: "en" }
// }
// },
// posts: { from: 5, to: 7 }
// }
// }Array Differences
const obj1 = {
tags: ["javascript", "react"],
scores: [85, 90, 78]
};
const obj2 = {
tags: ["javascript", "react", "nodejs"],
scores: [85, 92, 78, 88]
};
// Default array handling (replace)
const diff1 = objectDiff(obj1, obj2);
console.log(diff1);
// {
// tags: {
// from: ["javascript", "react"],
// to: ["javascript", "react", "nodejs"]
// },
// scores: {
// from: [85, 90, 78],
// to: [85, 92, 78, 88]
// }
// }
// Detailed array diff
const diff2 = objectDiff(obj1, obj2, { arrayDiff: 'detailed' });
console.log(diff2);
// {
// tags: {
// added: [{ index: 2, value: "nodejs" }],
// removed: [],
// modified: []
// },
// scores: {
// added: [{ index: 3, value: 88 }],
// removed: [],
// modified: [{ index: 1, from: 90, to: 92 }]
// }
// }Depth Control
const deepObj1 = {
level1: {
level2: {
level3: {
level4: {
value: "deep"
}
}
}
}
};
const deepObj2 = {
level1: {
level2: {
level3: {
level4: {
value: "deeper"
}
}
}
}
};
// Limit comparison depth
const shallowDiff = objectDiff(deepObj1, deepObj2, { depth: 2 });
console.log(shallowDiff);
// {
// level1: {
// level2: {
// from: { level3: { level4: { value: "deep" } } },
// to: { level3: { level4: { value: "deeper" } } }
// }
// }
// }
// Full depth comparison
const fullDiff = objectDiff(deepObj1, deepObj2);
console.log(fullDiff);
// {
// level1: {
// level2: {
// level3: {
// level4: {
// value: { from: "deep", to: "deeper" }
// }
// }
// }
// }
// }Ignoring Specific Keys
const config1 = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3,
timestamp: "2023-01-01T00:00:00Z",
sessionId: "abc123"
};
const config2 = {
apiUrl: "https://api.example.com",
timeout: 8000,
retries: 5,
timestamp: "2023-01-02T00:00:00Z",
sessionId: "def456"
};
// Ignore timestamp and sessionId (they always change)
const diff = objectDiff(config1, config2, {
ignoreKeys: ['timestamp', 'sessionId']
});
console.log(diff);
// {
// timeout: { from: 5000, to: 8000 },
// retries: { from: 3, to: 5 }
// }Including Equal Values
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { a: 1, b: 4, c: 3 };
// Default: only differences
const diffOnly = objectDiff(obj1, obj2);
console.log(diffOnly);
// { b: { from: 2, to: 4 } }
// Include equal values
const fullComparison = objectDiff(obj1, obj2, { includeEqual: true });
console.log(fullComparison);
// {
// a: { from: 1, to: 1, equal: true },
// b: { from: 2, to: 4, equal: false },
// c: { from: 3, to: 3, equal: true }
// }Configuration Management
// Track configuration changes
class ConfigManager {
constructor() {
this.currentConfig = {};
this.history = [];
}
updateConfig(newConfig) {
const diff = objectDiff(this.currentConfig, newConfig, {
ignoreKeys: ['lastModified', 'version']
});
if (Object.keys(diff).length > 0) {
this.history.push({
timestamp: new Date().toISOString(),
changes: diff,
previousConfig: { ...this.currentConfig }
});
this.currentConfig = { ...newConfig };
console.log('Configuration updated:', diff);
} else {
console.log('No configuration changes detected');
}
}
getChangeHistory() {
return this.history;
}
rollback(steps = 1) {
if (this.history.length >= steps) {
const targetHistory = this.history[this.history.length - steps];
this.currentConfig = { ...targetHistory.previousConfig };
this.history = this.history.slice(0, -steps);
console.log(`Rolled back ${steps} step(s)`);
}
}
}
// Usage
const configManager = new ConfigManager();
configManager.updateConfig({
database: { host: 'localhost', port: 5432 },
cache: { ttl: 3600 }
});
configManager.updateConfig({
database: { host: 'prod-db.com', port: 5432 },
cache: { ttl: 7200 },
logging: { level: 'info' }
});
console.log(configManager.getChangeHistory());Form State Tracking
// Track form changes for dirty state detection
class FormTracker {
constructor(initialData) {
this.initialData = { ...initialData };
this.currentData = { ...initialData };
}
updateField(fieldName, value) {
this.currentData[fieldName] = value;
}
getChanges() {
return objectDiff(this.initialData, this.currentData);
}
isDirty() {
const changes = this.getChanges();
return Object.keys(changes).length > 0;
}
getChangedFields() {
const changes = this.getChanges();
return Object.keys(changes);
}
reset() {
this.currentData = { ...this.initialData };
}
save() {
const changes = this.getChanges();
if (Object.keys(changes).length > 0) {
console.log('Saving changes:', changes);
this.initialData = { ...this.currentData };
return changes;
}
return null;
}
}
// Usage
const formTracker = new FormTracker({
name: 'John Doe',
email: 'john@example.com',
preferences: {
theme: 'dark',
notifications: true
}
});
formTracker.updateField('name', 'Jane Doe');
formTracker.updateField('preferences', {
theme: 'light',
notifications: true
});
console.log('Is dirty:', formTracker.isDirty()); // true
console.log('Changed fields:', formTracker.getChangedFields()); // ['name', 'preferences']
console.log('Changes:', formTracker.getChanges());
// {
// name: { from: 'John Doe', to: 'Jane Doe' },
// preferences: {
// theme: { from: 'dark', to: 'light' }
// }
// }