
Use case
I have a nested and complex JavaScript object, probably a back-end response or a complex UI state-tree. I can't ask the back-end to change this object. I need to find out if in this object, at any level of his complex and unordered hierarchy (which could be composed by simple values, other objects, arrays, arrays of objects, you name it...) there is at least one key-value pair of my choice.
Take the object below as an example:
//The back-end gives me a nested object with data in an unordered array representing all the features a user has.
let response = {
data: [
{
id: 3,
label: "CMS",
features: [],
},
{
id: 1,
label: "Admin",
features: [
{
id: 15,
label: "Users",
features: [],
},
{
id: 22,
label: "Reports",
features: [],
},
{
id: 25,
label: "Analytics",
features: [],
}
],
},
{
id: 4,
label: "Blog",
features: [],
},
{
id: 12,
label: "Payment",
features: [],
},
{
id: 7,
label: "Navigation",
features: [],
},
]
};
For example I want to check if the user has a feature, so I want to check if in that object there is a key-value pair as id : myFeatureId
. In this case I want to find if the specified user has access to the "Reports" feature, so I'll look for a key-value pair as
id : 22
.
Now to the code 
To accomplish this task I want to create a JavaScript pure function that receives the server response, the key I want to find, and the value that that key has to hold as inputs. I expect this function to return a boolean True
it the key-value pair was successfully found, otherwise
False
.
//The main function.
//
//fn( input, keyOfTheValue, valueToFind ) -> Bool
//
function findKeyValueRecursively( input, keyOfTheValue, valueToFind ){
//Some validation
if(typeof keyOfTheValue !== "string"){
throw new Error("Invalid parameter: keyOfTheValue has to be a string.");
}
//Accepts arrays of objects.
if ( Array.isArray(input) ) {
return input.reduce(function( result, element ){
if ( result === true ) {
return true;
}
//Recursive call.
return findKeyValueRecursively( element, keyOfTheValue, valueToFind );
}, false);
}
//Process objects.
if ( input !== null && typeof input === 'object' ) { //IMPORTANT: typeof null === 'object', this is a known JavaScript bug [1].
if ( keyOfTheValue in input && input[ keyOfTheValue ] === valueToFind ) { //See [2].
//We found it!
return true;
}
//Check for other nested objects or arrays.
for ( let key in input ) {
//The hasOwnProperty function is used to exclude properties found in the prototype chain.
if ( input.hasOwnProperty(key) ) {
//Recursive call: iterates through all the object properties.
if ( findKeyValueRecursively( input[ key ], keyOfTheValue, valueToFind ) ) {
return true;
}
}
}
}
return false;
}
Let's now create some objects to test different possible inputs and use cases:
//Simple object case.
let inputCase = {
a : 1,
b : null,
c : "Hello World!",
d : "",
e : false,
f : undefined,
};
findKeyValueRecursively( inputCase, 'a', 1) === true
findKeyValueRecursively( inputCase, 'a', 2) === false
findKeyValueRecursively( inputCase, 'a', false) === false
findKeyValueRecursively( inputCase, 'a', undefined) === false
findKeyValueRecursively( inputCase, 'b', 1) === false
//Nested object case.
inputCase = {
a : {
a : 1,
b : null,
c : "Hello World!",
},
b : null,
c : "Hello World!",
d : {
a : 2,
b : 1,
c : {
a : false,
b : null,
},
},
e : false,
f : undefined,
};
findKeyValueRecursively( inputCase, 'a', 1) === true
findKeyValueRecursively( inputCase, 'a', 2) === true
findKeyValueRecursively( inputCase, 'a', false) === true
findKeyValueRecursively( inputCase, 'a', undefined) === false
findKeyValueRecursively( inputCase, 'b', 1) === true
//Nested objects and arrays case.
inputCase = [
{
a : 0,
b : null,
c : "Hello World!",
},
{
a : 0,
b : null,
c : "Hello World!",
},
{
a : 2,
b : null,
c : [
{
a : [
{
a : false,
b : [],
c : "Hello World!",
}
],
b : null,
c : "Hello World!",
},
{
a : 1,
b : {
b : 1,
},
c : "Hello World!",
}
],
},
];
findKeyValueRecursively( inputCase, 'a', 1) === true
findKeyValueRecursively( inputCase, 'a', 2) === true
findKeyValueRecursively( inputCase, 'a', false) === true
findKeyValueRecursively( inputCase, 'a', undefined) === false
findKeyValueRecursively( inputCase, 'b', 1) === true
Footnotes
[1]: typeof null === 'object'
is a JavaScript known bug. It has not been fixed during these years because many websites rely on workarounds and expect that weird result, thus in order not to break them it has not been changed. Initially it was stated in the specifications:
11.4.3 The
typeof
Operator
The production UnaryExpression : typeof
UnaryExpression is evaluated as follows:
- Let val be the result of evaluating UnaryExpression.
- Type(val) is Reference, then:
- a. If IsUnresolvableReference(val) is
True"
, return"undefined"
. - b. Let val be GetValue(val).
- a. If IsUnresolvableReference(val) is
- Return a String determined by Type(val) according to Table 20.

[2]: keyOfTheValue in input
is needed in order to prevent reading a not existing key as the value undefined
(it can break the function if you search for something like a : undefined
).
Further readings
Why is typeof null === “object”?
The hasOwnProperty() Function
The reduce() Function
For updates, insights or suggestions, feel free to post a comment below!