I have two objects. Their structure looks a bit like this:
{
education: [\"school\", \"institute\"],
courses: [\"HTML\", \"JS\", \"CSS\"],
Computer: {
Just recursively check it:
function isContainedIn(a, b) {
if (typeof a != typeof b)
return false;
if (Array.isArray(a) && Array.isArray(b)) {
// assuming same order at least
for (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;
} else if (Object(a) === a) {
for (var p in a)
if (!(p in b && isContainedIn(a[p], b[p])))
return false;
return true;
} else
return 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) {
return isContainedIn(ael, bel);
});
});
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 equality
if (big && big.length) { // iterable, for example array, nodelist etc. (even string!)
if (small.length > big.length) return false; // small is bigger!
for (let i = 0; i < small.length; i++)
if (!sub(big[i], small[i])) // doesn't have a property
return false;
return true; // 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 conceptual
if (typeof small !== 'object' || small === null) return false;
// 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 property
return false;
continue;
}
return true;
}
return big === small; // primitive value type equality
};
or even use a much cleaner solution: https://github.com/blackflux/object-deep-contain
// When order of objects is not same
function isContainedIn(a, b) {
if (typeof a != typeof b)
return false;
if (Array.isArray(a) && Array.isArray(b)) {
if(a.length == 1) {
var j=0;
while (j < b.length) {
if ((isContainedIn( a[0], b[j]))) {
return true;
}
j++;
}
return false;
} else {
var k=0;
while (k < a.length) {
if (!(isContainedIn([a[k]], b))) {
return false;
}
k++;
}
return true;
}
} else if (Object(a) === a) {
for (var p in a)
if (!(p in b && isContainedIn(a[p], b[p])))
return false;
return true;
} else
return a === b;
};
isContainedIn(requirements, person)
true
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) return false; // small is bigger!
for(var i = 0; i < small.length; i++ ){
if(!sub(big[i],small[i])){ // doesn't have a property
return false;
}
}
return true; // 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 conceptual
if(typeof small !== "object" || small === null) return false;
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 property
return false;
}
return true;
}
}
return big === small; // primitive value type equality
// , or ES7 value type equality, future compat ftw :P
}
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);
return JSON.stringify(obj3) === JSON.stringify(obj1);
}
demo
Of course, another option would be to iterate over the entire object with vanilla JS.