2
\$\begingroup\$

I wrote a function to check if a nested object contains the key-value pair matched as parameter as following.

eq:
const obj ={
   "a":{
      "b":{
         "c1":["1","2"],
         "c2":"3"
      },
      "b2":"values",
      "b3": {"c5": "v"},
   },
   "a2":["v1","v2"],
   "a3":"v1"
}
checkNestedObjectByKeyValue(obj, "a3", "v1") // true
checkNestedObjectByKeyValue(obj, "a3", "v2") // false
checkNestedObjectByKeyValue(obj, "a2", ["v1","v2"]) // true
checkNestedObjectByKeyValue(obj, "a3", "v1") // true
checkNestedObjectByKeyValue(obj, "b3", {"c5": "v"}) // true

function checkNestedObjectByKeyValue(
  obj: Record<string, any>,
  objKey: string,
  objValue: string | Array<string>
): boolean {

  if (typeof obj !== 'object') return false;
  if (obj.hasOwnProperty(objKey)) {

    return JSON.stringify(obj[objKey]) === JSON.stringify(objValue)
  }
  for (const value of Object.values(obj)) {
    if (checkNestedObjectByKeyValue(value, objKey, objValue)) return true
  }
  return false
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ updated. code totally works \$\endgroup\$ Jun 29, 2020 at 18:59

2 Answers 2

1
\$\begingroup\$

Based on your description of the function, I would expect this test to return true, but your implementation returns false. So, either your description is unclear, or your implementation is buggy.

const obj ={
   "a":{
      "b":{
         "a3":"v2",
         "c1":["1","2"],
         "c2":"3"
      },
      "b2":"values",
      "b3": {"c5": "v"},
   },
   "a2":["v1","v2"],
   "a3":"v1"
}
checkNestedObjectByKeyValue(obj, "a3", "v2") // false, but I would expect true
\$\endgroup\$
1
  • 1
    \$\begingroup\$ we have "a3": "v1" as first level that is why it return false. not sure how i can handle that \$\endgroup\$ Jun 29, 2020 at 19:34
0
\$\begingroup\$

Main review

Your code works well in most cases, but the biggest change I recommend in order to skip over false positives would be to change the following lines

  if (obj.hasOwnProperty(objKey)) {
    return JSON.stringify(obj[objKey]) === JSON.stringify(objValue)
  }

to the following (move JSON.stringify(obj[objKey]) === JSON.stringify(objValue) into the conditional statement and return true):

  if (obj.hasOwnProperty(objKey) && JSON.stringify(obj[objKey]) === JSON.stringify(objValue)) {
    return true;
  }

As 200_success pointed out, your code returns false when a key exists in one part of the whole object but doesn't match and another key exists elsewhere which does match. This change will check whether a key matches first and proceeds to perform the recursive search for another key with matching value.

In the following code snippet, I changed the code to JavaScript so it could be run here in this post.

function checkNestedObjectByKeyValue(obj, objKey, objValue) {
  if (typeof obj !== "object") return false;
  if (obj.hasOwnProperty(objKey) && JSON.stringify(obj[objKey]) === JSON.stringify(objValue)) {
    return true;
  }
  for (const value of Object.values(obj)) {
    if (checkNestedObjectByKeyValue(value, objKey, objValue))
      return true;
  }
  return false;
}
const obj = {
  a: {
    b: {
      a3: "v2",
      c1: ["1", "2"],
      c2: "3"
    },
    b2: "values",
    b3: {
      c5: "v"
    },
  },
  a2: ["v1", "v2"],
  a3: "v1"
};
console.log(checkNestedObjectByKeyValue(obj, "a3", "v1"));
console.log(checkNestedObjectByKeyValue(obj, "a3", "v2"));
console.log(checkNestedObjectByKeyValue(obj, "a2", ["v1", "v2"]));
console.log(checkNestedObjectByKeyValue(obj, "b3", {
  c5: "v"
}));
// Added a check that should return false
console.log(checkNestedObjectByKeyValue(obj, "b2", "novalue"));

If the pattern of object properties is:

  • first level property name /^a\d*$/
  • second level property name /^b\d*$/
  • third level property name /^c\d*$/
  • etc.

Then this may not be necessary.

Other thoughts

When I first ran your code, I received an error: Argument of type '{ c5: string; }' is not assignable to parameter of type 'string | string[]'. which referenced the following line:

checkNestedObjectByKeyValue(obj, "b3", { c5: "v" })

I resolved this by adding | object to the objValue parameter as follows:

function checkNestedObjectByKeyValue(
  obj: Record<string, any>,
  objKey: string,
  objValue: string | Array<string> | object
): boolean {
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.