JS: Test Equality of Objects

By Xah Lee. Date: . Last updated: .

This page shows you a function to compare equality of objects by deep dive.

JavaScript doesn't have a buildin way to compare equality of 2 objects.

const c = {"a":3};
const d = {"a":3};

console.log( c === d ); // false

When a object is assigned to a variable, the variable holds a reference to the object.

If 2 variables hold the same reference, they are equal.

but 2 objects with same property and parent etc, will have different reference.

const x = {"a":3};
const y = x; // x and y holds the same reference

console.log( x === y ); // true

const z = {"a":3};

console.log( z === y ); // false

console.log( z === x ); // false

console.log(z); // {a:3}
console.log(y); // {a:3}

Function to Compare Object Equality

Here's functions to compare object equality. The object's enumerable properties are compared, one by one. If a property's value is object (or array), recursion is applied. Returns true if all enumerable property values are equal.

const xah_is_obj_equal = ((obj1, obj2) =>
{

/* [
    return true if 2 obj are equal.
    equal here means deep compare enumerable properties of object

    http://xahlee.info/js/js_comparison_equality_test_objects.html
    version 2017-09-24
 ] */

    const keys1 = Object.keys(obj1).sort();
    const keys2 = Object.keys(obj2).sort();

    if ( keys1.length !== keys2.length  ) {
        return false;
    }

    // first make sure have same keys. may save time
    if ( ! keys1.every( ((k, i) => (k === keys2[i])) ) ) {
        return false;
    }

    // check if any value is not equal
    return keys1.every ( ((kk) => {
        const v1 = obj1[kk];
        const v2 = obj2[kk];
        if ( Array.isArray(v1) )  {
            return xah_is_array_equal(v1,v2);
        } else if ( typeof v1 === "object" && v1 !== null) {
            return xah_is_obj_equal(v1,v2);
        } else {
            return  v1 === v2;
        }
    })  );
});

const xah_is_array_equal = ((array1, array2) =>
{

/* [
    return true if 2 array are equal
    allow array-like object
    allow nested array

    http://xahlee.info/js/js_comparison_equality_test_objects.html
    version 2017-09-24
 ] */

    // allow array-like object
    if ( Array.isArray(array1) !== Array.isArray(array2) ) { return false; }
    if (array1.length !== array2.length) { return false; }

    return Array.prototype.every.call(
        array1,
        ((x, i) => {
            const y = array2[i];
            if ( Array.isArray(x) ) {
                if ( ! Array.isArray(y) ) {
                    return false;}
                else {
                    return xah_is_array_equal(x, y); }
            } else if ( typeof x === "object" && typeof x !== null) {
                if (! ( typeof y === "object" && typeof y !== null)) {
                    return false;}
                else {
                    return xah_is_obj_equal(x,y); }
            } else {
                return (x === y);
            }
        })
    );
});

// ------------------------------------------------------
// tests

console.log(
    // simple obj
    xah_is_obj_equal({"a":1}, {"a":1}),

    // diff value
    ! xah_is_obj_equal({"a":1}, {"a":2}),

    // num of obj not same
    ! xah_is_obj_equal({"a":1,"b":2}, {"a":1}),

    // diff order
    xah_is_obj_equal({"a":1,"b":2}, {"b":2,"a":1}),

    // test empty obj
    xah_is_obj_equal({}, {}),

    // nested obj
    xah_is_obj_equal(
        {"a":1,"b":{"c":3}},
        {"a":1,"b":{"c":3}})
);

// nested obj with diff value
console.log(
    ! xah_is_obj_equal(
        {"a":1,"b":{"c":1}},
        {"a":1,"b":{"c":2}})
,
// deeper nesting
    xah_is_obj_equal(
        {"a":1,"b":{"c":3, "mm":{"x1":1,"x2":{"x3":3}}}},
        {"a":1,"b":{"c":3, "mm":{"x1":1,"x2":{"x3":3}}}})
,
// works on array-like obj
    xah_is_obj_equal(
        {0:3,1:4,"length":2},
        {0:3,1:4,"length":2})
,
// works on array obj
    xah_is_obj_equal(
        [3, {0:3,1:4,"length":2}, [[2,7],9]],
        [3, {0:3,1:4,"length":2}, [[2,7],9]])
);

// ----------------------------------------------

// test on array objects
console.log (
    xah_is_array_equal([3,4], [3,4])
    ,
    ! xah_is_array_equal([3,4], [4,3])
    ,
    ! xah_is_array_equal([3,4], [3])
    ,
    xah_is_array_equal([], [])
    ,
    // array-like objects
    xah_is_array_equal(
        {0:3,1:4,"length":2},
        {0:3,1:4,"length":2}
    )
    ,
    // nested array
    xah_is_array_equal(
        [3,4, [[2,7],9]],
        [3,4, [[2,7],9]])
    ,
    // negation
    ! xah_is_array_equal(
        [3,4, {"t":2}],
        [3,4, {"t":2, "b":2}])
    ,
    xah_is_array_equal(
        [ {"b":1},4],
        [ {"b":1},4])
);

Pre ES2015 Version

function xah_is_obj_equal (obj1, obj2) {
    // return true if 2 obj are equal.
    // equal here means deep compare enumerable properties of object

    // http://xahlee.info/js/js_comparison_equality_test_objects.html
    // version 2016-04-20

    const keys1 = Object.keys(obj1).sort();
    const keys2 = Object.keys(obj2).sort();

    if ( keys1.length !== keys2.length  ) {
        return false;
    }

    // first make sure have same keys. may save time
    if ( ! keys1.every( function(k, i) { return (k === keys2[i]); } ) ) {
        return false;
    }

    // check if any value is not equal
    return keys1.every (function(kk) {
        const v1 = obj1[kk];
        const v2 = obj2[kk];
        if ( Array.isArray(v1) )  {
            return xah_is_array_equal(v1,v2);
        } else if ( typeof v1 === "object" && v1 !== null) {
            return xah_is_obj_equal(v1,v2);
        } else {
            return  v1 === v2;
        }
    } );
}

function xah_is_array_equal (array1, array2) {
    // return true if 2 array are equal
    // allow array-like object
    // allow nested array

    // http://xahlee.info/js/js_comparison_equality_test_objects.html
    // version 2016-04-20

    // allow array-like object
    if ( Array.isArray(array1) !== Array.isArray(array2) ) { return false; }
    if (array1.length !== array2.length) { return false; }

    return Array.prototype.every.call(
        array1,
        function(x, i) {
            const y = array2[i];
            if ( Array.isArray(x) ) {
                if ( ! Array.isArray(y) ) {
                    return false;}
                else {
                    return xah_is_array_equal(x, y); }
            } else if ( typeof x === "object" && typeof x !== null) {
                if (! ( typeof y === "object" && typeof y !== null)) {
                    return false;}
                else {
                    return xah_is_obj_equal(x,y); }
            } else {
                return (x === y);
            }
        }
    );
}

// ------------------------------------------------------
// tests

console.log(
    // simple obj
    xah_is_obj_equal({"a":1}, {"a":1}),

    // diff value
    ! xah_is_obj_equal({"a":1}, {"a":2}),

    // num of obj not same
    ! xah_is_obj_equal({"a":1,"b":2}, {"a":1}),

    // diff order
    xah_is_obj_equal({"a":1,"b":2}, {"b":2,"a":1}),

    // test empty obj
    xah_is_obj_equal({}, {}),

    // nested obj
    xah_is_obj_equal(
        {"a":1,"b":{"c":3}},
        {"a":1,"b":{"c":3}})
);

// nested obj with diff value
console.log(
    ! xah_is_obj_equal(
        {"a":1,"b":{"c":1}},
        {"a":1,"b":{"c":2}})
,
// deeper nesting
    xah_is_obj_equal(
        {"a":1,"b":{"c":3, "mm":{"x1":1,"x2":{"x3":3}}}},
        {"a":1,"b":{"c":3, "mm":{"x1":1,"x2":{"x3":3}}}})
,
// works on array-like obj
    xah_is_obj_equal(
        {0:3,1:4,"length":2},
        {0:3,1:4,"length":2})
,
// works on array obj
    xah_is_obj_equal(
        [3, {0:3,1:4,"length":2}, [[2,7],9]],
        [3, {0:3,1:4,"length":2}, [[2,7],9]])
);

// ----------------------------------------------

// test on array objects
console.log (
    xah_is_array_equal([3,4], [3,4])
    ,
    ! xah_is_array_equal([3,4], [4,3])
    ,
    ! xah_is_array_equal([3,4], [3])
    ,
    xah_is_array_equal([], [])
    ,
    // array-like objects
    xah_is_array_equal(
        {0:3,1:4,"length":2},
        {0:3,1:4,"length":2}
    )
    ,
    // nested array
    xah_is_array_equal(
        [3,4, [[2,7],9]],
        [3,4, [[2,7],9]])
    ,
    // negation
    ! xah_is_array_equal(
        [3,4, {"t":2}],
        [3,4, {"t":2, "b":2}])
    ,
    xah_is_array_equal(
        [ {"b":1},4],
        [ {"b":1},4])
);

Comparison by JSON, Ordering Problem

If you turn object into JSON string, then compare the string, this is not reliable because object properties is not ordered.

// comparison by JSON.stringify is not reliable

const x = {"a":1, "b":2};
const y = {"b":2, "a":1}; // reverse order

console.log( JSON.stringify(x) === JSON.stringify(y) ); // false

see also JS: Compare Array Equality

Array Topic

  1. JS: Array Basics
  2. JS: Understand JS Array
  3. JS: Create Array
  4. JS: Sparse Array
  5. JS: Array-Like Object
  6. JS: Array How-To

  1. JS: Array Object
  2. JS: Array.prototype

Object and Inheritance Topic

  1. JS: Object System Overview
  2. JS: What's Object?
  3. JS: Prototype and Inheritance
  4. JS: Create Object
  5. JS: Object Literal Expression
  6. JS: Find Object's Prototype
  7. JS: Set Object's Prototype
  8. JS: How to Create Object with Parent X?
  9. JS: Prevent Adding Property
  10. JS: Determine Type of Object
  11. JS: Primitive Value Object Wrapper
  12. JS: Clone, Deep Copy Object/Array
  13. JS: Test Equality of Objects

  1. JS: Object Object
  2. JS: Object.prototype
Liket it? Put $1 at patreon.

Or, Buy JavaScript in Depth