Unable to create a fully recursive function that checks if a value exists in a nested object
tried Implementing the code in JavaScript(ES6+) but only managed to do it semi-recursive with a for loop to check the contents of key at a time
function contains(obj, value) {
for (var key in obj) {
if (typeof obj[key] === "object") {
return contains(obj[key], value);
}
if (obj[key] === value) {
return true;
}
}
return false;
}
Dvir Rozenblat is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
2
Your example is quite lacking… But I’ll try to help you with what’s available…
If you want to verify, and just verify, if an object has some value as one of its enumerable properties value, then you can do just this:
Object.values(Object(obj)).includes(val);
NOTE: Object.values()
return an array of the enumerable properties values only.
If you want to verify all properties (including non-enumerable properties), then you could do something like this:
function Contains ( Subject, Value, Hidden = true, Prototype = true, Verifieds = [] )
{
let Current = Object(Subject);
if ( ! Array.isArray(Verifieds) )
{
Verifieds = [Verifieds];
}
do
{
// Adds every object in the prototype chain to the `Verifieds` parameter.
Verifieds.push(Current);
for ( const Property of ( Hidden ? Reflect.ownKeys(Current) : Object.keys(Current) ) )
{
// Is the `Value` in the shallowest level?
if ( Object.is(Value, Current[Property]) )
{
return true;
}
// The `Value` nests other values?
if ( ( Current[Property] !== null ) && ( typeof Current[Property] == 'object' ) )
{
// If the current property value was verified...
if ( Verifieds.includes(Current[Property]) )
{
// ... then continue to the next property.
continue;
}
else
{
// ... Else, add the current property to the `Verifieds` parameter.
Verifieds.push(Current[Property]);
}
// If the current property value nested the `Value`...
if ( Contains(Current[Property], Value, Hidden, Prototype, Verifieds) )
{
return true;
}
}
}
Current = Object.getPrototypeOf(Current);
// Verify if `Value` is in the prototype chain.
if ( Prototype && Object.is(Current, Value) )
{
return true;
}
}
while ( Current !== null );
return false;
}
// Simple object
var Obj1 = { AnyProperty: 3 };
console.log( 'Obj1 =', Obj1 );
console.log( '3?', Contains(Obj1, 3) ); // outputs: 3? true
console.log( '2?', Contains(Obj1, 2) ); // outputs: 2? false
// Simple object with non enumerable property
var Obj2 = { [ Symbol('Non Enumerable Property') ]: 5 };
console.log( 'Obj2 =', Obj2 );
console.log( '5?', Contains(Obj2, 5) ); // outputs: 5? true
console.log( '4?', Contains(Obj2, 4) ); // outputs: 4? false
console.log( '3?', Contains(Obj2, 3) ); // outputs: 3? false
console.log( '2?', Contains(Obj2, 2) ); // outputs: 2? false
// Obj2 didn't inherit Obj1 in the code above.
// But now Obj2 inherits Obj1.
Object.setPrototypeOf(Obj2, Obj1);
// Obj3 inherits Obj2 that inherits Obj1.
var Obj3 = Object.create(Obj2);
console.log( 'Obj3 =', Obj3 );
console.log( '5?', Contains(Obj3, 5) ); // outputs: 5? true
console.log( '4?', Contains(Obj3, 4) ); // outputs: 4? false
console.log( '3?', Contains(Obj3, 3) ); // outputs: 3? true
console.log( '2?', Contains(Obj3, 2) ); // outputs: 2? false
// Obj4 contains Obj1, Obj2 and itself.
var Obj4 = { Obj1, Obj2, get Self () { return Obj4; } };
// Note that here Obj2 inherits Obj1.
console.log( 'Obj4 =', Obj4 );
console.log( 'Obj1?', Contains(Obj4, Obj1) ); // outputs: Obj1? true
console.log( 'Obj2?', Contains(Obj4, Obj2) ); // outputs: Obj2? true
console.log( 'Obj3?', Contains(Obj4, Obj3) ); // outputs: Obj3? false
console.log( 'Obj4?', Contains(Obj4, Obj4) ); // outputs: Obj4? true
// Verifying the nested values...
console.log( '5?', Contains(Obj4, 5) ); // outputs: 5? true
console.log( '4?', Contains(Obj4, 4) ); // outputs: 4? false
console.log( '3?', Contains(Obj4, 3) ); // outputs: 3? true
console.log( '2?', Contains(Obj4, 2) ); // outputs: 2? false
// Whoops... Obj5 contains Obj3 that inherits Obj2 that inherits Obj1.
var Obj5 = { Obj3 };
console.log( 'Obj5 =', Obj5 );
console.log( 'Obj1?', Contains(Obj5, Obj1) ); // outputs: Obj1? true
console.log( 'Obj2?', Contains(Obj5, Obj2) ); // outputs: Obj2? true
console.log( 'Obj3?', Contains(Obj5, Obj3) ); // outputs: Obj3? true
console.log( 'Obj4?', Contains(Obj5, Obj4) ); // outputs: Obj4? false
// Verifying the nested values...
console.log( '5?', Contains(Obj5, 5) ); // outputs: 5? true
console.log( '4?', Contains(Obj5, 4) ); // outputs: 4? false
console.log( '3?', Contains(Obj5, 3) ); // outputs: 3? true
console.log( '2?', Contains(Obj5, 2) ); // outputs: 2? false
console.log( 'Excluding the hidden values...' );
console.log( 'Obj1?', Contains(Obj5, Obj1, false) ); // outputs: Obj1? true
console.log( 'Obj2?', Contains(Obj5, Obj2, false) ); // outputs: Obj2? true
console.log( 'Obj3?', Contains(Obj5, Obj3, false) ); // outputs: Obj3? true
console.log( 'Obj4?', Contains(Obj5, Obj4, false) ); // outputs: Obj4? false
console.log( '5?', Contains(Obj5, 5, false) ); // outputs: 5? false
console.log( '4?', Contains(Obj5, 4, false) ); // outputs: 4? false
console.log( '3?', Contains(Obj5, 3, false) ); // outputs: 3? true
console.log( '2?', Contains(Obj5, 2, false) ); // outputs: 2? false
console.log( 'And then excluding the the prototype chain...' );
console.log( 'Obj1?', Contains(Obj5, Obj1, false, false) ); // outputs: Obj1? false
console.log( 'Obj2?', Contains(Obj5, Obj2, false, false) ); // outputs: Obj2? false
console.log( 'Obj3?', Contains(Obj5, Obj3, false, false) ); // outputs: Obj3? true
console.log( 'Obj4?', Contains(Obj5, Obj4, false, false) ); // outputs: Obj4? false
console.log( '5?', Contains(Obj5, 5, false, false) ); // outputs: 5? false
console.log( '4?', Contains(Obj5, 4, false, false) ); // outputs: 4? false
console.log( '3?', Contains(Obj5, 3, false, false) ); // outputs: 3? true
console.log( '2?', Contains(Obj5, 2, false, false) ); // outputs: 2? false
NOTE: I added a Hidden
parameter with default value of true
. This means that if you not specify false
as the third parameter it will verify all properties including those that are not enumerables. You can invert this behavior by changing the default to false
.
NOTE: The Prototype
parameter with the default value of true
tells the function that if Value
is a prototype of Subject
or one of its nested values it must return true
. This does not means that if you use false
as this parameter value the function will not verify the prototype chain (it always does). See the Obj5
example for details.
NOTE: The Verifieds
parameter is there to prevent infinite looping when Subject
nests itself.
3