2

I have already reviewed some of the answers to similar questions, however, I want to ask my question differently.

Let's say we have a string like "level1.level2.level3. ..." that indicates a nested property in an object called Obj.

The point is that we may not know how many nested properties exist in this string. For instance, it may be "level1.level2" or "level1.level2.level3.level4".

Now, I want to write a function, that given the Obj and the string of properties as input, to simply tell us if such a nested property exists in the object or not (let's say true or false as output).


Update: Thanks to @Silvinus, I found the solution with a minor modification:

        private checkNestedProperty(obj, props) {
        var splitted = props.split('.');
        var temp = obj;
        for (var index in splitted) {
            if (temp[splitted[index]] === 'undefined' || !temp[splitted[index]]) return false;
            temp = temp[splitted[index]];
        }
        return true;
    }
Pedram
  • 2,421
  • 4
  • 31
  • 49
  • @Pedram, you want to get "report" about existence of each property or just for the most(last) nested one? – RomanPerekhrest May 04 '16 at 06:39
  • @RomanPerekhrest In fact, the most (last) one. However, it is obvious that if each of these properties does not exist, the last one does not exist as well :) – Pedram May 04 '16 at 06:45
  • I am wondering why my question should get a down vote! – Pedram May 04 '16 at 07:27

4 Answers4

4

You could use Array#every() and thisArg of it, by iterating the keys and checking if it is in the given object.

var fn = function (o, props) {
    return props.split('.').every(k => k in o && (o = o[k], true));
}

console.log(fn({}, "toto.tata"));                                   // false
console.log(fn({ toto: { tata: 17 } }, "toto.tata"));               // true
console.log(fn({ toto: { tata: { tutu: 17 } } }, "toto.foo.tata")); // false
console.log(fn({ toto: { tata: false } }, "toto.tata")); // true
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
3

You can explore your Obj with this function :

var fn = function(obj, props) {
        var splited = props.split('.');
        var temp = obj;
        for(var index in splited) {
            if(typeof temp[splited[index]] === 'undefined') return false;
            temp = temp[splited[index]]
        }
           return true
        }

var result = fn({ }, "toto.tata");
console.log(result); // false

var result = fn({ toto: { tata: 17 } }, "toto.tata");
console.log(result); // true

var result = fn({ toto: { tata: { tutu: 17 } } }, "toto.foo.tata");
console.log(result); // false

This function allow to explore nested property of Obj that depends of props passed in parameter

Silvinus
  • 1,445
  • 8
  • 16
  • This answer fails on the case `fn({a:false}, 'a')`. Minor point, but there is no need for the variable `temp` here. –  May 04 '16 at 06:23
  • Also, shouldn't it be `temp = temp[splited[index]];`? For that reason, the test case `fn({a: {b: { c: 1 }}}, 'a.b.c')` causes a run-time error "Cannot read property 'c' of undefined". –  May 04 '16 at 06:29
  • For the first comment, ... I agree. I write this code so fast. I edited it and change my test by if(typeof temp[splited[index]] === 'undefined') return false; For the second, sorry it's a mistake. temp = obj[splited[index]] must be replace by temp = temp[splited[index]] – Silvinus May 04 '16 at 06:48
  • @Silvinus Thank you, I have modified your answer (minor mistake) and put the correct function in my update. – Pedram May 04 '16 at 06:54
1

This answer provides the basic answer to your question. But it needs to be tweaked to handle the undefined case:

function isDefined(obj, path) {
  function index(obj, i) { 
    return obj && typeof obj === 'object' ? obj[i] : undefined; 
  }

  return path.split(".").reduce(index, obj) !== undefined;
}
Community
  • 1
  • 1
0

Based on the solution given by @Silvinus here is a solution if you deal with array inside nested objects (as it is often the case in results from databases queries) :

checkNested = function(obj, props) {
    var splited = props.split('.');
    var temp = obj;
    for(var index in splited) {
      var regExp = /\[([^)]+)\]/;
      var matches = regExp.exec(splited[index])
      if(matches) {
        splited[index] = splited[index].replace(matches[0], '');
      }
      if(matches) {
        if(matches && typeof temp[splited[index]][matches[1]] === 'undefined') return false;
            temp = temp[splited[index]][matches[1]];
      }
      else {
            if(!matches && typeof temp[splited[index]] === 'undefined') return false;
                temp = temp[splited[index]]
      }
    }
    return true
}

obj = {ok: {ao: [{},{ok: { aa: ''}}]}}

console.log(checkNested(obj, 'ok.ao[1].ok.aa')) // ==> true
console.log(checkNested(obj, 'ok.ao[0].ok.aa')) // ==> false
Ostn
  • 803
  • 1
  • 9
  • 27