smart-json-diff
v1.0.0
Published
A modern, compact JSON comparison library with path-aware output and multiple output modes
Downloads
9
Maintainers
Readme
smart-json-diff
A modern, compact JSON comparison library that generates clean, structured, and developer-friendly diff output with explicit path tracking.
🚀 Features
| Feature | Description | | ---------------------------- | ------------------------------------------------------ | | 🔍 Deep Comparison | Compare nested objects and arrays recursively | | 🧭 Path-Aware | Always includes explicit paths in diff results | | 🆚 Changes Only | Shows only what changed, ignoring identical properties | | ⚙️ Multiple Output Modes | Array, object, or callback-based output | | 🧪 Type Detection | Detects and reports data type changes | | 🎯 Custom Filtering | Filter which paths to compare or skip | | 📊 Summary Mode | Get summary statistics of changes | | 🔒 TypeScript Native | Full TypeScript support with type safety |
📦 Installation
npm install smart-json-diff🌟 Quick Start
import { smartJsonDiff } from 'smart-json-diff';
const before = {
user: { name: 'John', age: 25 },
tags: ['dev', 'javascript'],
active: true,
};
const after = {
user: { name: 'Jane', age: 25 },
tags: ['dev', 'typescript'],
active: false,
};
const diff = smartJsonDiff(before, after);
console.log(diff);
/*
[
{ path: 'user.name', old: 'John', new: 'Jane' },
{ path: 'tags[1]', old: 'javascript', new: 'typescript' },
{ path: 'active', old: true, new: false }
]
*/📖 Usage Examples
Basic Array Output (Default)
import { smartJsonDiff } from 'smart-json-diff';
const diff = smartJsonDiff(objectA, objectB);
// Returns: DiffResult[]Object Output Mode
const diff = smartJsonDiff(objectA, objectB, { output: 'object' });
/*
Returns:
{
'user.name': { old: 'John', new: 'Jane' },
'tags[1]': { old: 'javascript', new: 'typescript' }
}
*/Callback Mode
smartJsonDiff(objectA, objectB, {
output: 'callback',
onChange: (path, oldVal, newVal) => {
console.log(`${path}: ${oldVal} → ${newVal}`);
},
});With Path Filtering
const a = { name: 'John', age: 30, city: 'NY' };
const b = { name: 'Jane', age: 31, city: 'LA' };
const diff = smartJsonDiff(a, b, {
filterPath: (path) => path === 'name' || path === 'city', // Only include specific paths
});
console.log(diff);
/*
[
{ path: 'name', old: 'John', new: 'Jane' },
{ path: 'city', old: 'NY', new: 'LA' }
]
// Note: 'age' changes are excluded by the filter
*/Summary Information
import { diffSummary, hasChanges } from 'smart-json-diff';
const summary = diffSummary(objectA, objectB);
console.log(summary);
/*
{
totalChanges: 3,
paths: ['user.name', 'tags[1]', 'active']
}
*/
const changed = hasChanges(objectA, objectB);
console.log(changed); // true🔧 API Reference
smartJsonDiff(a, b, options?)
Parameters:
a: any- First object to compareb: any- Second object to compareoptions?: SmartJsonDiffOptions- Configuration options
Returns:
DiffResult[]- Array of changes (default)Record<string, DiffEntry>- Object keyed by path (whenoutput: 'object')void- Nothing (whenoutput: 'callback')
Options
interface SmartJsonDiffOptions {
output?: 'array' | 'object' | 'callback';
onChange?: (path: string, oldVal: any, newVal: any) => void;
filterPath?: (path: string) => boolean;
compareArraysByIndex?: boolean; // default: true
strict?: boolean; // default: true
}Option Details:
output: Format of the returned diff resultsonChange: Callback function called for each difference (only whenoutput: 'callback')filterPath: Function to filter which paths should be included in results. Returntrueto include the path,falseto exclude itcompareArraysByIndex: Whether to compare arrays by index position (default:true)strict: Use strict equality (===) vs loose equality (==) for comparison (default:true)
Types
interface DiffResult {
path: string;
old: any;
new: any;
}
interface DiffEntry {
old: any;
new: any;
}Utility Functions
// Get summary of changes
diffSummary(a: any, b: any): { totalChanges: number; paths: string[] }
// Check if objects have any differences
hasChanges(a: any, b: any, options?: SmartJsonDiffOptions): boolean🎯 Advanced Usage
Nested Object Comparison
const complex1 = {
company: {
departments: [
{ name: 'Engineering', budget: 100000 },
{ name: 'Marketing', budget: 50000 },
],
founded: 2020,
},
};
const complex2 = {
company: {
departments: [
{ name: 'Engineering', budget: 120000 },
{ name: 'Sales', budget: 60000 },
],
founded: 2020,
},
};
const diff = smartJsonDiff(complex1, complex2);
/*
[
{ path: 'company.departments[0].budget', old: 100000, new: 120000 },
{ path: 'company.departments[1].name', old: 'Marketing', new: 'Sales' },
{ path: 'company.departments[1].budget', old: 50000, new: 60000 }
]
*/Advanced Path Filtering
The filterPath option allows you to control which paths are included in the final results:
const data1 = {
user: { name: 'John', email: '[email protected]' },
system: { version: '1.0', build: 123 },
metadata: { created: '2023-01-01', updated: '2023-01-02' },
};
const data2 = {
user: { name: 'Jane', email: '[email protected]' },
system: { version: '1.1', build: 124 },
metadata: { created: '2023-01-01', updated: '2023-01-03' },
};
// Only include user-related changes
const userChanges = smartJsonDiff(data1, data2, {
filterPath: (path) => path.startsWith('user.'),
});
/*
[
{ path: 'user.name', old: 'John', new: 'Jane' },
{ path: 'user.email', old: '[email protected]', new: '[email protected]' }
]
*/
// Include multiple specific paths
const specificChanges = smartJsonDiff(data1, data2, {
filterPath: (path) => path === 'user.name' || path === 'system.version',
});
/*
[
{ path: 'user.name', old: 'John', new: 'Jane' },
{ path: 'system.version', old: '1.0', new: '1.1' }
]
*/Type Change Detection
const before = { id: 123, active: 'true' };
const after = { id: '123', active: true };
const diff = smartJsonDiff(before, after);
/*
[
{ path: 'id', old: 123, new: '123' },
{ path: 'active', old: 'true', new: true }
]
*/Handling Special Values
const before = { value: null, missing: undefined };
const after = { value: 0, missing: null, new: 'added' };
const diff = smartJsonDiff(before, after);
/*
[
{ path: 'value', old: null, new: 0 },
{ path: 'missing', old: undefined, new: null },
{ path: 'new', old: undefined, new: 'added' }
]
*/⚙️ Configuration Options
Strict vs Loose Comparison
// Strict mode (default) - 1 !== '1'
const strictDiff = smartJsonDiff({ num: 1 }, { num: '1' });
// Result: [{ path: 'num', old: 1, new: '1' }]
// Loose mode - 1 == '1'
const looseDiff = smartJsonDiff({ num: 1 }, { num: '1' }, { strict: false });
// Result: [] (no differences)Array Comparison Modes
// Index-based comparison (default)
const diff1 = smartJsonDiff(['a', 'b'], ['b', 'a']);
// Result: [{ path: '[0]', old: 'a', new: 'b' }, { path: '[1]', old: 'b', new: 'a' }]
// Note: Unordered array comparison is planned for future releases🧪 Testing & Edge Cases
The library handles various edge cases:
- ✅ Empty objects and arrays
- ✅ Null and undefined values
- ✅ Boolean values
- ✅ Number vs string comparison
- ✅ Functions and symbols (ignored)
- ✅ Deeply nested structures
- ✅ Circular references (basic support)
🚀 Performance
- Lightweight: ~3KB minified + gzipped
- Zero dependencies
- Optimized for common use cases
- Memory efficient for large objects
🛠️ Development
# Clone the repository
git clone https://github.com/amrulizwan/smart-json-diff.git
cd smart-json-diff
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm test
# Development with watch mode
npm run dev🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
📝 Changelog
v1.0.0
- Initial release
- Deep object comparison
- Multiple output modes
- Path-aware results
- TypeScript support
- Custom filtering
- Summary utilities
📄 License
MIT © Amrul Izwan
🔗 Links
Made with ❤️ for developers who need clean, reliable JSON comparison.
