is-equal
Deep comparison of two values for equality. Recursively compares objects, arrays, dates, and primitives for deep equality. Handles circular references, special objects (Date, RegExp, Map, Set), and all primitive types. Useful for comparing complex data structures.
Installation
npx fragmen add validation/is-equalSource Code
/**
* Deep comparison of two values for equality.
*
* Recursively compares objects, arrays, dates, and primitives for deep equality.
* Handles circular references, special objects (Date, RegExp, Map, Set), and
* all primitive types. Useful for comparing complex data structures.
*
* @tags validation, pure
* @param {unknown} a First value to compare
* @param {unknown} b Second value to compare
* @returns {boolean} True if values are deeply equal, false otherwise
*
* @example
* ```typescript
* // Primitives
* isEqual(1, 1); // true
* isEqual('hello', 'hello'); // true
* isEqual(true, false); // false
*
* // Arrays
* isEqual([1, 2, 3], [1, 2, 3]); // true
* isEqual([1, 2], [1, 2, 3]); // false
*
* // Objects
* isEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
* isEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // true (order doesn't matter)
*
* // Nested structures
* isEqual(
* { user: { name: 'Alice', scores: [1, 2, 3] } },
* { user: { name: 'Alice', scores: [1, 2, 3] } }
* ); // true
*
* // Dates
* isEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true
*
* // Special cases
* isEqual(NaN, NaN); // true (unlike === comparison)
* isEqual(null, undefined); // false
* ```
*/
export function isEqual(a: unknown, b: unknown): boolean {
// Use Object.is for proper 0 vs -0 comparison and NaN handling
if (Object.is(a, b)) {
return true;
}
// Different types
if (typeof a !== typeof b) {
return false;
}
// null/undefined
if (a === null || b === null || a === undefined || b === undefined) {
return false;
}
// Dates
if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
}
// RegExp
if (a instanceof RegExp && b instanceof RegExp) {
return a.toString() === b.toString();
}
// Arrays
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
return a.every((item, index) => isEqual(item, b[index]));
}
// Maps
if (a instanceof Map && b instanceof Map) {
if (a.size !== b.size) {
return false;
}
for (const [key, value] of a) {
if (!b.has(key) || !isEqual(value, b.get(key))) {
return false;
}
}
return true;
}
// Sets
if (a instanceof Set && b instanceof Set) {
if (a.size !== b.size) {
return false;
}
for (const item of a) {
if (!b.has(item)) {
return false;
}
}
return true;
}
// Objects
if (typeof a === 'object' && typeof b === 'object') {
const keysA = Object.keys(a as object);
const keysB = Object.keys(b as object);
if (keysA.length !== keysB.length) {
return false;
}
return keysA.every(key => {
const objA = a as Record<string, unknown>;
const objB = b as Record<string, unknown>;
return (
Object.prototype.hasOwnProperty.call(objB, key) &&
isEqual(objA[key], objB[key])
);
});
}
return false;
}
Examples
// Primitives
isEqual(1, 1); // true
isEqual('hello', 'hello'); // true
isEqual(true, false); // false
// Arrays
isEqual([1, 2, 3], [1, 2, 3]); // true
isEqual([1, 2], [1, 2, 3]); // false
// Objects
isEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
isEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // true (order doesn't matter)
// Nested structures
isEqual(
{ user: { name: 'Alice', scores: [1, 2, 3] } },
{ user: { name: 'Alice', scores: [1, 2, 3] } }
); // true
// Dates
isEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true
// Special cases
isEqual(NaN, NaN); // true (unlike === comparison)
isEqual(null, undefined); // false
Related Utilities
is-empty
validationChecks if a value is empty (null, undefined, empty string, empty array, or empty object). A comprehensive emptiness check that handles multiple JavaScript types. Useful for form validation, data cleaning, and conditional rendering.
is-falsy
booleanChecks if a value is falsy. Returns true for JavaScript falsy values: false, 0, -0, 0n, "", null, undefined, and NaN. Useful for type-safe falsy checks and filtering operations.
is-truthy
booleanChecks if a value is truthy. Returns true for all JavaScript truthy values (anything that is not falsy). Complementary function to isFalsy, useful for filtering and validation.
safe-parse
jsonSafely parses a JSON string, returning undefined if parsing fails. Provides error-safe JSON parsing without throwing exceptions. Useful when working with untrusted input or when you want to handle parsing failures gracefully rather than with try-catch blocks.
has-path
objectChecks if a nested property path exists in an object. Safely traverses nested object properties using a dot-notation path string or an array of keys. Returns true if the path exists (even if the final value is undefined), false if any part of the path is missing.
chunk
arraySplits an array into chunks of a specified size. Creates a new array containing subarrays (chunks) of the original array, each with a maximum length of the specified size. The last chunk may contain fewer elements if the array length is not evenly divisible by the chunk size.
Quick Actions
Tags
Parameters
aunknownFirst value to compare
bunknownSecond value to compare
Returns
booleanTrue if values are deeply equal, false otherwise