loose-deep-equal
v0.0.2
Published
JavaScript fast deep equality, but treating missing/undefined properties as equal
Downloads
8,201
Maintainers
Readme
loose-deep-equal
Fast deep equality comparison that treats missing properties as equal to undefined.
Why?
Standard deep equality functions treat these objects as different:
const obj1 = { a: 1 };
const obj2 = { a: 1, b: undefined };But in many real-world scenarios (API responses, optional fields, configuration objects), you want these to be considered equal. That's what loose-deep-equal does.
Features
- 🚀 Fast - Optimized for performance with early exits and minimal overhead
- 🎯 Loose equality - Missing properties are treated as
undefined - 📦 Zero dependencies - Small and self-contained
- 🔧 Drop-in replacement - Compatible with other deep equality functions
- 🆕 ES6+ Support - Full support for Maps, Sets, TypedArrays, and BigInt
Installation
npm install loose-deep-equalUsage
CommonJS
const looseEqual = require('loose-deep-equal');
looseEqual({ a: 1 }, { a: 1, b: undefined }); // trueES Modules
// Default import
import looseEqual from 'loose-deep-equal';
// Named import
import { looseEqual } from 'loose-deep-equal';
looseEqual({ a: 1 }, { a: 1, b: undefined }); // trueExamples
// Missing properties are treated as undefined
looseEqual({ a: 1 }, { a: 1, b: undefined }); // true
// Regular deep equality still works
looseEqual({ a: { b: 2 } }, { a: { b: 2 } }); // true
// null is not undefined
looseEqual({ a: null }, { a: undefined }); // false
looseEqual({ a: null }, {}); // false
// Works with arrays
looseEqual([1, 2], [1, 2]); // true
// Works with nested structures
looseEqual(
{ user: { name: 'John' } },
{ user: { name: 'John', age: undefined } }
); // true
// ES6 types are fully supported
looseEqual(new Map([[1, 2]]), new Map([[1, 2]])); // true
looseEqual(new Set([1, 2, 3]), new Set([3, 2, 1])); // trueComparison with other libraries
| Scenario | loose-deep-equal | fast-deep-equal | lodash.isEqual |
|----------|-------------------|-------------------|------------------|
| {a: 1} vs {a: 1, b: undefined} | ✅ true | ❌ false | ❌ false |
| {a: null} vs {a: undefined} | ❌ false | ❌ false | ❌ false |
| Simple equal objects | 71.8% speed | 100% (baseline) | 13.4% speed |
| Objects with undefined | 33.5% speed | 100% (baseline) | 19.0% speed |
| Large objects (100+ props) | 96.5% speed | 100% (baseline) | 81.5% speed |
| ES6 Maps/Sets support | ✅ Full | ✅ Full | ✅ Full |
| TypedArray support | ✅ Full | ✅ Full | ✅ Full |
When to use this
Use loose-deep-equal when:
- Comparing API responses where fields might be omitted or explicitly set to
undefined - Working with configuration objects with optional properties
- Implementing state management where undefined and missing are semantically equivalent
- Migrating between APIs that handle optional fields differently
When NOT to use this
Don't use loose-deep-equal when:
- You need to distinguish between missing properties and
undefined - You're working with data where
undefinedhas special meaning - You need strict equality semantics
How it works
The algorithm:
- If objects have the same number of keys, uses standard fast comparison
- If different number of keys, checks all properties from both objects
- Missing properties are treated as
undefinedduring comparison - Handles all standard JavaScript types including Date, RegExp, typed arrays, etc.
Performance
loose-deep-equal is highly optimized for performance. Here are the benchmark results:
Performance vs fast-deep-equal
| Scenario | Performance | Details | |----------|-------------|---------| | Simple equal objects | 71.8% | Objects with same structure | | Nested objects | 85.1% | Deep object hierarchies | | Large objects (100+ properties) | 96.5% | Nearly identical performance | | Objects with undefined properties | 33.5% | Our special case - checking all keys |
Key optimizations:
- Fast path when objects have same number of keys (70-96% of original speed)
- Efficient double-loop algorithm for different key counts
- Early exit on first difference
- No intermediate Set creation for key comparison
- Native support for ES6 types without performance penalty
Benchmark details:
- Still significantly faster than
lodash.isEqualin all cases (3-6x faster) - Much faster than the
deep-equallibrary (100-1000x faster) - Minimal overhead from Map/Set/TypedArray support
Edge cases handled
- ✅ Objects with null prototype
- ✅ Objects with overridden
hasOwnProperty - ✅ Sparse arrays (holes treated as
undefined) - ✅ Symbol properties (ignored, like other libraries)
- ✅ Non-enumerable properties (ignored)
- ⚠️ Arrays with extra properties: Only numeric indices are compared, extra properties are ignored. This matches
fast-deep-equalbehavior.const arr1 = [1, 2, 3]; arr1.customProp = 'value1'; const arr2 = [1, 2, 3]; arr2.customProp = 'value2'; looseEqual(arr1, arr2); // true - extra properties ignored
API
looseEqual(a, b)
Compares two values for loose deep equality.
Parameters:
a(any) - First valueb(any) - Second value
Returns: boolean - True if values are loosely equal
Example:
const looseEqual = require('loose-deep-equal');
looseEqual({ x: 1 }, { x: 1, y: undefined }); // true
looseEqual([1, 2, 3], [1, 2, 3]); // true
looseEqual(null, undefined); // falseTypeScript
TypeScript definitions are included:
import looseEqual from 'loose-deep-equal';
const result: boolean = looseEqual({ a: 1 }, { a: 1, b: undefined });Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
Credits
Based on the excellent fast-deep-equal by @epoberezkin.
