/ 4 min read
Git-Style Diff for Strings, Objects, and Arrays
In today’s fast-paced development world, having a tool that can clearly and concisely show you exactly what changed — whether in simple strings, nested objects, or complex arrays — is a game-changer.
That’s exactly why diff-leven was born. Inspired by the simplicity of Levenshtein’s algorithm and craving clarity in complex data diffs.
What is diff-leven?
diff-leven is a lightweight npm package for comparing:
- Strings
- Objects
- Arrays
- Primitive values (numbers, booleans, etc.)
🎮 Playground: link
📖 Background: Why Levenshtein Distance?
The Levenshtein distance measures the minimum number of single-character edits — insertions, deletions, or substitutions — required to transform one string into another. It’s been foundational in spell checkers since its introduction by Levenshtein. By extending this algorithm to objects and arrays, diff-leven lets you compare complex data structures with the same precision and clarity you’d expect from a Git diff.
🔍 What Makes diff-leven Stand Out?
1. Multi-Type Support
- Objects & Nested Structures: Detect changes deep in your data tree.
- Arrays: Smart content-based matching highlights moves, additions, and deletions in lists.
- Strings: Character-level diff with optional similarity percentages.
- Primitives: Even numbers and booleans are first-class citizens.
2. Git-Style, Colorized Output
By default, diff-leven presents differences with clear ”+” (additions) and ”–” (deletions) markers, complete with ANSI colors for terminal readability.
3. Flexible Configuration
Options like ‘keysOnly’, ‘ignoreKeys’, ‘ignoreValues’, and ‘outputKeys’ let you tailor the diff to your needs—focus on structure, skip timestamps, or force inclusion of critical fields.
🚀 Getting Started
Install
npm install diff-levenHow to Use
const { diff, diffRaw, isDiff } = require('diff-leven');
// Basic diff (string output)console.log(diff({ foo: 'bar' }, { foo: 'baz' }));// Output:// {// - foo: "bar"// + foo: "baz"// }
// Raw diff objectconst rawDiff = diffRaw({ foo: 'bar' }, { foo: 'baz' });console.log(JSON.stringify(rawDiff, null, 2));// Output:// {// "type": "changed",// "path": [],// "oldValue": { "foo": "bar" },// "newValue": { "foo": "baz" },// "children": [// {// "type": "changed",// "path": ["foo"],// "oldValue": "bar",// "newValue": "baz"// }// ]// }
// Boolean diff checkconsole.log(isDiff({ foo: 'bar' }, { foo: 'baz' }));// Output: true
console.log(isDiff({ foo: 'bar' }, { foo: 'bar' }));// Output: false
// With optionsconsole.log( isDiff( { foo: 'bar', timestamp: 123 }, { foo: 'bar', timestamp: 456 }, { ignoreKeys: ['timestamp'] }, ),);// Output: false (identical when ignoring timestamp)
// No colorsconsole.log(diff({ foo: 'bar' }, { foo: 'baz' }, { color: false }));// Output:// {// - foo: "bar"// + foo: "baz"// }
// Full outputconsole.log(diff({ foo: 'bar', b: 3 }, { foo: 'baz', b: 3 }, { full: true }));// Output:// {// - foo: "bar"// + foo: "baz"// b: 3// }
// Ignore keysconsole.log( diff({ foo: 'bar', b: 3 }, { foo: 'baz', b: 3 }, { ignoreKeys: ['b'] }),);// Output:// {// - foo: "bar"// + foo: "baz"// }
// Ignore valuesconsole.log( diff({ foo: 'bar', b: 3 }, { foo: 'baz', b: 3 }, { ignoreValues: true }),);// Output showing structural differences only
// Show similarity info for string changesconsole.log( diff('hello world', 'hello there', { color: true, withSimilarity: true }),);// Output:// - 'hello world'// + 'hello there' (73% similar)
// Output specific keysconsole.log( diff({ foo: 'bar', b: 3 }, { foo: 'baz', b: 3 }, { outputKeys: ['foo'] }),);// Output:// {// - foo: "bar"// + foo: "baz"// }
// Combine optionsconsole.log( diff( { foo: 'bar', b: 3 }, { foo: 'baz', b: 3 }, { keysOnly: true, ignoreKeys: ['b'], ignoreValues: true, outputKeys: ['foo'], full: true, color: false, }, ),);Explore Examples: Check out the basic example script to see more patterns in action.
⚙️ Options Matrix
| Option | Type | Default | Description |
|---|---|---|---|
color | boolean | true | Use colorized output |
keysOnly | boolean | false | Only compare object keys |
full | boolean | false | Output the entire object tree |
outputKeys | string[] | [] | Always include these keys in output |
ignoreKeys | string[] | [] | Ignore these keys when comparing |
ignoreValues | boolean | false | Ignore value differences, focus on structure |
withSimilarity | boolean | false | Show similarity info for string changes |
💡 Real-World Use Case: API Regression Detector
Imagine you’re maintaining a client library. You fetch an API response:
const oldResponse = await fetch(...).then(res => res.json());const newResponse = ...;
console.log(diff(oldResponse, newResponse));Instantly see if a ratio changed, a property is missing, or an endpoint returned unexpected structure.
✨ Conclusion
Whether you’re debugging a tricky migration, reviewing API changes, or just curious about how two versions of your data differ, diff-leven delivers clear, colorized diffs in seconds. By marrying the classic Levenshtein algorithm with a flexible JavaScript API, it empowers developers to visualize changes across any serializable structure.
Ready to give it a spin? Install it today and let the diffing begin! 🛠️🎉