Home > Back-end >  Returning value corresponding to a searched key in a deeply nested Javascript object
Returning value corresponding to a searched key in a deeply nested Javascript object

Time:06-06

I have an object like below in Javascript,

const obj = {
      "group1": {
        "sub_group1_1": {
          "inner_sub_group1_1_1": {
            "name": "abc"
          },
          "inner_sub_group1_1_2": {
            "name": "def"
          }
        },
        "sub_group1_2": {
          "inner_sub_group1_2_1": {
            "name": "ghi"
          },
          "inner_sub_group1_2_2": {
            "name": "jkl" 
          }
        }
      },
      "group2": {
        "sub_group2_1": {
          "inner_sub_group2_1_1": {
            "name": "mno"
          },
          "inner_sub_group2_1_2": {
            "name": "pqr"
          }
        },
        "sub_group2_2": {
          "inner_sub_group2_2_1": {
            "name": "stu"
          },
          "inner_sub_group2_2_2": {
            "name": "wxy" 
          }
        }
      }
    }

I want to write a function which could search for a key in the above object and return me the corresponding value of that key, for e.g.,

Input

filterObject(obj, 'inner_sub_group2_2_2')

Output

{
   "name": "wxy
}

Input

filterObject(obj, 'inner_sub_group1_1_1')

Output

{
   "name": "abc
}

Approach

I have written a recursive function for the same, but it doesn't seem to work for the second scenario,

  const filterObject = (obj, searchedKey) => {
    let result = {};
    for (const key in obj) {
      const currentObj = obj[key];
      if (key === searchedKey) {
        result = currentObj;
        break;
      } else if (typeof currentObj === 'object') {
        result = filterObject(currentObj, searchedKey);
      }
    }
    return result;
  };

Any sort of help would be highly appreciated. Thanks!

CodePudding user response:

You need to be able to check whether the recursive call found something, which you aren't doing - right now, you're just reassigning result, and then ignoring it and going on to the next iteration of the loop.

An approach that would work for this particular situation would be to check to see if the resulting object has any keys.

const obj={group1:{sub_group1_1:{inner_sub_group1_1_1:{name:"abc"},inner_sub_group1_1_2:{name:"def"}},sub_group1_2:{inner_sub_group1_2_1:{name:"ghi"},inner_sub_group1_2_2:{name:"jkl"}}},group2:{sub_group2_1:{inner_sub_group2_1_1:{name:"mno"},inner_sub_group2_1_2:{name:"pqr"}},sub_group2_2:{inner_sub_group2_2_1:{name:"stu"},inner_sub_group2_2_2:{name:"wxy"}}}};

const filterObject = (obj, searchedKey) => {
  if (searchedKey in obj) {
    return obj[searchedKey];
  }
  for (const key in obj) {
    const currentObj = obj[key];
    if (typeof currentObj === 'object') {
      const result = filterObject(currentObj, searchedKey);
      if (Object.keys(result).length) return result;
    }
  }
  return {};
};

console.log(filterObject(obj, 'inner_sub_group2_2_2'));
console.log(filterObject(obj, 'inner_sub_group1_1_1'));

But that won't work if the value found isn't an object, or if the found object is empty. (A different object reference unconnected with the original structure would be returned.)

For the more general situation, the recursive call should return two things: whether the nested value was found or not, and the found value (if any). One approach to this is to return an object if something was found, and nothing otherwise.

const obj={group1:{sub_group1_1:{inner_sub_group1_1_1:{name:"abc"},inner_sub_group1_1_2:{name:"def"}},sub_group1_2:{inner_sub_group1_2_1:{name:"ghi"},inner_sub_group1_2_2:{name:"jkl"}}},group2:{sub_group2_1:{inner_sub_group2_1_1:{name:"mno"},inner_sub_group2_1_2:{name:"pqr"}},sub_group2_2:{inner_sub_group2_2_1:{name:"stu"},inner_sub_group2_2_2:{name:"wxy"}}}};

const filterObject = (obj, searchedKey) => {
  if (searchedKey in obj) {
    return { result: obj[searchedKey] };
  }
  for (const key in obj) {
    const currentObj = obj[key];
    if (typeof currentObj === 'object') {
      const result = filterObject(currentObj, searchedKey);
      if (result) return result;
    }
  }
};

console.log(filterObject(obj, 'inner_sub_group2_2_2').result);
console.log(filterObject(obj, 'inner_sub_group1_1_1').result);

CodePudding user response:

The issue in your code is that if the recursive call is successful, the loop still continues, with another recursive call being made that may not be successful, and so the good result is lost.

You need an if after that recursive call that detects success and breaks out if it is one.

Now, to make the distinction between failure and success you'll make things much easier if you return null when there is no success.

Two lines need to be adjusted:

  const filterObject = (obj, searchedKey) => {
    let result = null; // To indicate nothing found yet
    for (const key in obj) {
      const currentObj = obj[key];
      if (key === searchedKey) {
        result = currentObj;
        break;
      } else if (typeof currentObj === 'object') {
        result = filterObject(currentObj, searchedKey);
        if (result) break; // <--- break out when success!
      }
    }
    return result;
  };

A little improvement might be needed on your is-object test, because typeof null === 'object' is a true expression.

You can use this instead:

if (Object(currentObj) === currentObj)
  • Related