Deep Object Diff
A fast, lightweight TypeScript library for comparing deeply nested objects and detecting changes with detailed diff information.
Features
- 🔍 Deep Object Comparison - Compare objects of any depth and complexity
- 🚀 High Performance - Optimized with circular reference detection
- 🎯 Detailed Output - Get precise information about what changed, where, and how
- ⚙️ Configurable - Customize comparison behavior with options
- 🛠️ Utility Functions - Built-in helpers for filtering and analyzing diffs
- 📦 Zero Dependencies - Lightweight and tree-shakeable
- 🎨 TypeScript Support - Full type safety and IntelliSense
Installation
npm install @astech/deepdiff
Basic Usage
import { compare } from "deepdiff";
const objA = {
user: {
name: "Alice",
age: 30,
hobbies: ["reading", "gaming"],
},
};
const objB = {
user: {
name: "Alice Smith",
age: 31,
hobbies: ["reading", "hiking"],
},
};
const diffs = compare(objA, objB);
console.log(diffs);
// Output:
// [
// {
// path: "user.name",
// type: "changed",
// before: "Alice",
// after: "Alice Smith",
// summary: 'Changed user.name from "Alice" to "Alice Smith"',
// description: 'Field "user" → "name" changed from "Alice" to "Alice Smith".'
// },
// {
// path: "user.age",
// type: "changed",
// before: 30,
// after: 31,
// summary: "Changed user.age from 30 to 31",
// description: 'Field "user" → "age" changed from 30 to 31.'
// },
// {
// path: "user.hobbies.[1]",
// type: "changed",
// before: "gaming",
// after: "hiking",
// summary: 'Changed user.hobbies.[1] from "gaming" to "hiking"',
// description: 'Field "user" → "hobbies" → index 1 changed from "gaming" to "hiking".'
// }
// ]
Advanced Configuration
Compare Options
import { compare, CompareOptions } from "deepdiff";
const options: CompareOptions = {
// Include null/undefined values in the diff
includeNulls: true,
// Custom equality function
customEqual: (a, b) => Math.abs(a - b) <= 1,
// Maximum depth to traverse (prevents stack overflow)
maxDepth: 10,
// Keys to ignore during comparison
ignoreKeys: ["timestamp", "id"],
// Whether to ignore case for string comparisons
ignoreCase: true,
};
const diffs = compare(objA, objB, options);
Example Use Cases
// Ignore specific fields
const diffs = compare(userA, userB, {
ignoreKeys: ["lastModified", "version"],
});
// Case-insensitive comparison
const diffs = compare(configA, configB, {
ignoreCase: true,
});
// Custom tolerance for numbers
const diffs = compare(dataA, dataB, {
customEqual: (a, b) => {
if (typeof a === "number" && typeof b === "number") {
return Math.abs(a - b) < 0.01; // 1% tolerance
}
return a === b;
},
});
Utility Functions
Filtering Diffs
import { compare, filterByType, groupByType } from "deepdiff";
const diffs = compare(objA, objB);
// Filter by change type
const added = filterByType(diffs, "added");
const removed = filterByType(diffs, "removed");
const changed = filterByType(diffs, "changed");
// Group all diffs by type
const groups = groupByType(diffs);
console.log(groups);
// {
// added: [...],
// removed: [...],
// changed: [...]
// }
Summary Statistics
import { compare, getSummary } from "deepdiff";
const diffs = compare(objA, objB);
const summary = getSummary(diffs);
console.log(summary);
// {
// total: 5,
// added: 2,
// removed: 1,
// changed: 2
// }
Quick Checks
import { compare, isDeepEqual, getChangedPaths } from "deepdiff";
// Check if objects are deeply equal
const areEqual = isDeepEqual(objA, objB);
// Get just the paths that changed
const changedPaths = getChangedPaths(diffs);
// ['user.name', 'user.age', 'user.hobbies.[1]']
Error Handling
The library provides clear error messages for invalid inputs:
// Missing objects
compare(undefined, objB); // Error: "Both objects must be provided for comparison"
// Non-objects
compare("string", objB); // Error: "Both parameters must be objects for comparison"
// Null values
compare(null, objB); // Error: "Both objects must be provided for comparison"
Performance Features
- Circular Reference Detection: Prevents infinite loops
- Depth Limiting: Configurable maximum depth to prevent stack overflow
- Efficient Traversal: Uses generators for memory efficiency
- Early Termination: Stops comparison when objects are identical
API Reference
compare(a, b, options?)
Main comparison function.
Parameters:
a
(object): First object to compareb
(object): Second object to compareoptions
(CompareOptions, optional): Configuration options
Returns: DiffResult[]
CompareOptions
interface CompareOptions {
includeNulls?: boolean; // Include null/undefined values
customEqual?: (a: any, b: any) => boolean; // Custom equality function
maxDepth?: number; // Maximum traversal depth
ignoreKeys?: string[]; // Keys to ignore
ignoreCase?: boolean; // Case-insensitive string comparison
}
DiffResult
interface DiffResult {
path: string; // Dot-notation path to the change
type: "added" | "removed" | "changed";
before?: any; // Previous value
after?: any; // New value
summary: string; // Human-readable summary
description: string; // Detailed description
}
Utility Functions
filterByType(diffs, type)
- Filter diffs by change typegroupByType(diffs)
- Group diffs by typegetSummary(diffs)
- Get summary statisticsisDeepEqual(a, b, options?)
- Check if objects are equalgetChangedPaths(diffs)
- Get array of changed paths
CLI Usage
# Compare two JSON files
npx deepdiff file1.json file2.json
# Compare with options
npx deepdiff file1.json file2.json --ignore-keys timestamp,id --max-depth 5
License
MIT