Deep-EQL is an improved deep equality testing library for Node.js and browsers that performs recursive comparison of object keys and values beyond referential equality. It handles complex scenarios including circular references, special object types, and provides advanced customization options.
npm install deep-eqlimport deepEqual from "deep-eql";
import { MemoizeMap } from "deep-eql";import deepEqual from "deep-eql";
// Basic object comparison
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
console.log(deepEqual(obj1, obj2)); // true
// Handles special cases like NaN and ±0
console.log(deepEqual(NaN, NaN)); // true
console.log(deepEqual(-0, +0)); // false
// Works with complex nested structures
const complex1 = {
array: [1, 2, { nested: true }],
date: new Date('2023-01-01'),
regex: /test/gi,
set: new Set([1, 2, 3])
};
const complex2 = {
array: [1, 2, { nested: true }],
date: new Date('2023-01-01'),
regex: /test/gi,
set: new Set([1, 2, 3])
};
console.log(deepEqual(complex1, complex2)); // trueDeep-EQL uses a multi-layered comparison strategy:
Primary function for comparing two values with deep equality semantics.
/**
* Assert deeply nested sameValue equality between two objects of any type
* @param {any} leftHandOperand - First value to compare
* @param {any} rightHandOperand - Second value to compare
* @param {DeepEqualOptions} [options] - Optional configuration
* @returns {boolean} - true if deeply equal, false otherwise
*/
function deepEqual(leftHandOperand, rightHandOperand, options);
interface DeepEqualOptions {
/** Custom equality comparator function */
comparator?: (left: any, right: any) => boolean | null;
/** Memoization object for circular reference handling, or false to disable */
memoize?: MemoizeMap | false;
}Usage Examples:
import deepEqual from "deep-eql";
// Basic usage
deepEqual({ a: 1 }, { a: 1 }); // true
deepEqual([1, 2, 3], [1, 2, 3]); // true
// With custom comparator
deepEqual(
{ timestamp: new Date('2023-01-01') },
{ timestamp: new Date('2023-01-02') },
{
comparator: (left, right) => {
if (left instanceof Date && right instanceof Date) {
return left.getFullYear() === right.getFullYear();
}
return null; // Fall back to default comparison
}
}
); // true
// Disable memoization for performance in simple cases
deepEqual(simpleObj1, simpleObj2, { memoize: false });Memoization implementation for circular reference detection and performance optimization.
/**
* Memoization map implementation that uses WeakMap when available,
* otherwise falls back to FakeMap for environments without WeakMap support
*/
const MemoizeMap: typeof WeakMap;
interface MemoizeMapInterface {
/**
* Retrieve cached comparison result
* @param {any} key - The key to look up
* @returns {any} - The cached value or undefined
*/
get(key: any): any;
/**
* Store comparison result in cache
* @param {any} key - The key to store under
* @param {any} value - The value to store
*/
set(key: any, value: any): void;
}Usage Examples:
import { MemoizeMap } from "deep-eql";
// Create custom memoization map
const memoMap = new MemoizeMap();
// Use with deepEqual for consistent caching across calls
deepEqual(obj1, obj2, { memoize: memoMap });
deepEqual(obj1, obj3, { memoize: memoMap }); // Reuses cacheAll typed array types supported with element-by-element comparison:
Full support for TC39 Temporal API proposal:
// Temporal objects with .equals() method
deepEqual(
Temporal.PlainDate.from('2023-01-01'),
Temporal.PlainDate.from('2023-01-01')
); // Uses .equals() method
// Temporal.Duration uses total nanoseconds
deepEqual(
Temporal.Duration.from({ hours: 1 }),
Temporal.Duration.from({ minutes: 60 })
); // true
// Temporal.TimeZone and Calendar use string representation
deepEqual(
Temporal.TimeZone.from('America/New_York'),
Temporal.TimeZone.from('America/New_York')
); // trueNaN === NaN (true, unlike standard equality)-0 !== +0 (false, unlike standard equality)Only specific properties compared regardless of enumerability:
name propertymessage propertycode propertyArguments objects are treated as distinct from Arrays:
function example() {
deepEqual([], arguments); // false
deepEqual([], Array.from(arguments)); // true
}Automatic detection and handling of circular references:
const obj1 = { name: 'test' };
obj1.self = obj1;
const obj2 = { name: 'test' };
obj2.self = obj2;
deepEqual(obj1, obj2); // true - handles circular referencesThe library gracefully handles comparison failures and edge cases:
Optimization Examples:
// Fast path for primitives and simple references
deepEqual(5, 5); // Immediate return
deepEqual("hello", "hello"); // Immediate return
// Memoization for complex objects
const complexObj = { /* large nested structure */ };
deepEqual(complexObj, complexObj); // Cached after first comparison
// Disable memoization for simple comparisons to avoid overhead
deepEqual(simpleObj1, simpleObj2, { memoize: false });