Skip to content Skip to sidebar Skip to footer

Object Comparing: Check If An Object Contains The Whole Other Object

I have two objects. Their structure looks a bit like this: { education: ['school', 'institute'], courses: ['HTML', 'JS', 'CSS'], Computer: { 'OS':'WXP', 'WS':'No

Solution 1:

Just recursively check it:

functionisContainedIn(a, b) {
    if (typeof a != typeof b)
        returnfalse;
    if (Array.isArray(a) && Array.isArray(b)) {
        // assuming same order at leastfor (var i=0, j=0, la=a.length, lb=b.length; i<la && j<lb;j++)
            if (isContainedIn(a[i], b[j]))
                i++;
        return i==la;
    } elseif (Object(a) === a) {
        for (var p in a)
            if (!(p in b && isContainedIn(a[p], b[p])))
                returnfalse;
        returntrue;
    } elsereturn a === b;
}

> isContainedIn(requirements, person)
true

For a more set-logic-like approach to arrays, where order does not matter, add something like

        a.sort();
        b = b.slice().sort()

(assuming orderable contents) before the array comparison loop or replace that by the quite inefficient

return a.every(function(ael) {
            return b.some(function(bel) {
                returnisContainedIn(ael, bel);
            });
        });

Solution 2:

JavaScript (in ES5) has two composite native types (I'm assuming you don't have any custom collections in your code, if you do - I assume they support the 'old' iteration protocol (having .length)

Here is an annotated sketch of a solution. I did not run this - it's there to get you an idea of how to implement this algorithm. Note that this enters an endless loop for back references (var a = {}; a.a =a}).

function sub(big,small){
    if(typeof big === "function") return small === big; // function reference equality.if(big.length){ // iterable, for example array, nodelist etc. (even string!)if(small.length > big.length) returnfalse; // small is bigger!for(var i = 0; i < small.length; i++ ){
            if(!sub(big[i],small[i])){ // doesn't have a propertyreturnfalse;
            }
        }
        returntrue; // all properties are subproperties recursively
    }
    if(typeof big === "object" && big !== null){
        // I assume null is not a subset of an object, you may change this, it's conceptualif(typeof small !== "object" || small === null) returnfalse; 
        for(var key in small){
            // I consider the prototype a part of the object, you may filter this with a // hasOwnProperty check here.if(!sub(big[key],small[key])){ // doesn't have a propertyreturnfalse;
            }
            returntrue;
        }
    }
    return big === small; // primitive value type equality// , or ES7 value type equality, future compat ftw :P
}

Solution 3:

Edit: didn't notice that merge changes the first argument... changed the code, but it still would cause obj2 to change. You can add _.cloneDeep(obj2) which should take care of that, but by then my solution doesn't seem as elegant. Updated the demo with cloneDeep as well.

Edit2: Since JSON.stringify requires the order of object properties to be the same in the objects you compare, you could instead use something like Object comparison in JavaScript. However, in the demo you can see that it works, so I would say there is a good chance that for your case, using _.merge with JSON.stringify is reliable.

With lo-dash, you can use _.merge and check whether the result is the same as the larger object.

function(obj1, obj2) {
    var obj3 =_.merge(_.cloneDeep(obj2), obj1);
    returnJSON.stringify(obj3) === JSON.stringify(obj1);
}

demo

Of course, another option would be to iterate over the entire object with vanilla JS.

Solution 4:

// When order of objects is not same

function isContainedIn(a, b) {
    if (typeof a != typeof b)
        returnfalse;
    if (Array.isArray(a) && Array.isArray(b)) {
        if(a.length == 1) {
            var j=0;
            while (j < b.length) {
                if ((isContainedIn( a[0], b[j]))) {
                    returntrue;
                }
                j++;
            }
            returnfalse;
        } else {
            var k=0;
            while (k < a.length) {
                if (!(isContainedIn([a[k]], b))) {
                    returnfalse;
                }
                k++;
            }
            returntrue;
        }
    } elseif (Object(a) === a) {
        for (var p in a)
            if (!(p in b && isContainedIn(a[p], b[p])))
                returnfalse;
        returntrue;
    } elsereturn a === b;
};


isContainedIn(requirements, person)
true

Solution 5:

In addition to Benjamin's answer - you could test this:

const sub = (big, small) => {
    if (typeof big === 'function' || typeof small === 'string') return small === big; // function or string reference equalityif (big && big.length) { // iterable, for example array, nodelist etc. (even string!)if (small.length > big.length) returnfalse; // small is bigger!for (let i = 0; i < small.length; i++)
            if (!sub(big[i], small[i])) // doesn't have a propertyreturnfalse;
        returntrue; // all properties are subproperties recursively
    }
    if (typeof big === 'object' && big !== null) {
        // I assume null is not a subset of an object, you may change this, it's conceptualif (typeof small !== 'object' || small === null) returnfalse;
        // console.log(Object.keys(small));for (const key of Object.keys(small)) {
            // I consider the prototype a part of the object, you may filter this with a// hasOwnProperty check here.if (sub(big[key], small[key]) === false) // doesn't have a propertyreturnfalse;
            continue;
        }
        returntrue;
    }
    return big === small; // primitive value type equality
};

or even use a much cleaner solution: https://github.com/blackflux/object-deep-contain

Post a Comment for "Object Comparing: Check If An Object Contains The Whole Other Object"