@astech/deepdiff
Version:
A fast, lightweight TypeScript library for comparing deeply nested objects and detecting changes with detailed diff information.
262 lines (198 loc) • 6.12 kB
Markdown
# 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
```bash
npm install @astech/deepdiff
```
## Basic Usage
```typescript
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
```typescript
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
```typescript
// 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
```typescript
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
```typescript
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
```typescript
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:
```typescript
// 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 compare
- `b` (object): Second object to compare
- `options` (CompareOptions, optional): Configuration options
**Returns:** `DiffResult[]`
### `CompareOptions`
```typescript
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`
```typescript
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 type
- `groupByType(diffs)` - Group diffs by type
- `getSummary(diffs)` - Get summary statistics
- `isDeepEqual(a, b, options?)` - Check if objects are equal
- `getChangedPaths(diffs)` - Get array of changed paths
## CLI Usage
```bash
# 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