20

Let's say we have this JavaScript object:

var object = {
   innerObject:{
       deepObject:{
           value:'Here am I'
       }
   }
};

How can we check if value property exists?

I can see only two ways:

First one:

if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
    console.log('We found it!');
}

Second one:

if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
    console.log('We found it too!');
}

But is there a way to do a deep check? Let's say, something like:

object['innerObject.deepObject.value']

or

object.hasOwnProperty('innerObject.deepObject.value')
2
  • Sure, using any of a number libraries that support that. Commented Oct 30, 2015 at 20:54
  • You can easily write a function that takes a string like that, splits it into an array of property names, and goes into a loop checking whether each property exists. Commented Oct 30, 2015 at 20:56

9 Answers 9

26

There isn't a built-in way for this kind of check, but you can implement it easily. Create a function, pass a string representing the property path, split the path by ., and iterate over this path:

Object.prototype.hasOwnNestedProperty = function(propertyPath) {
  if (!propertyPath)
    return false;

  var properties = propertyPath.split('.');
  var obj = this;

  for (var i = 0; i < properties.length; i++) {
    var prop = properties[i];

    if (!obj || !obj.hasOwnProperty(prop)) {
      return false;
    } else {
      obj = obj[prop];
    }
  }

  return true;
};

// Usage:
var obj = {
  innerObject: {
    deepObject: {
      value: 'Here am I'
    }
  }
}

console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));

Sign up to request clarification or add additional context in comments.

Comments

18

You could make a recursive method to do this.

The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns false.

var obj = {
  innerObject: {
    deepObject: {
      value: 'Here am I'
    }
  }
};

function hasOwnDeepProperty(obj, prop) {
  if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
    if (obj.hasOwnProperty(prop)) {              // if this object already contains the property, we are done
      return true;
    }
    for (var p in obj) {                         // otherwise iterate on all the properties of this object.
      if (obj.hasOwnProperty(p) &&               // and as soon as you find the property you are looking for, return true
          hasOwnDeepProperty(obj[p], prop)) { 
        return true;
      }
    }
  }
  return false;                                  
}

console.log(hasOwnDeepProperty(obj, 'value'));   // true
console.log(hasOwnDeepProperty(obj, 'another')); // false

7 Comments

I'm afraid this function will look for property in any inner object, not specific.
Oh, I guess I misunderstood you. Well, Viktor Bahtev's answer is the way to go then.
@nem nice broadly usable solution nonetheless!
Yeah, may be it was not the best example. I have to iterate over some objects with errors and each of this objects has same property 'message'. Your example is great, but not for my case :)
So long as you don't have any circular references.
|
3

Alternative recursive function:

Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.

Otherwise, it returns an array with true, false, false for any key with the name propName.

The .reduce then rolls up the array through an or statement.

function deepCheck(obj,propName) {
  if obj.hasOwnProperty(propName) {             // Performance improvement (thanks to @nem's solution)
    return true;
  }
  return Object.keys(obj)                       // Turns keys of object into array of strings
    .map(prop => {                              // Loop over the array
      if (typeof obj[prop] == 'object') {       // If property is object,
        return deepCheck(obj[prop],propName);   // call recursively
      } else {
        return (prop == propName);              // Return true or false
      }
    })                                          // The result is an array like [false, false, true, false]
    .reduce(function(previousValue, currentValue, index, array) {
      return previousValue || currentValue;
    }                                           // Do an 'or', or comparison of everything in the array.
                                                // It returns true if at least one value is true.
  )
}

deepCheck(object,'value'); // === true

PS: nem035's answer showed how it could be more performant: his solution breaks off at the first found 'value.'

Comments

3

My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)

JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.

So this function just wraps the callback function inside a try catch statement. If it catches the exception returns false.

var obj = {
  innerObject: {
    deepObject: {
      value: 'Here am I'
    }
  }
};

const validate = (cb) => {
  try {
    return cb();
  } catch (e) {
    return false;
  }
}


if (validate(() => obj.innerObject.deepObject.value)) {
 // Is going to work
}


if (validate(() => obj.x.y.z)) {
 // Is not going to work
}

When it comes to performance, it's hard to say which approach is better. On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.

But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.

See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/

You can also check the library I wrote here: https://github.com/yatki/try-to-validate

Comments

1

I use try-catch:

var object = {
   innerObject:{
       deepObject:{
           value:'Here am I'
       }
   }
};
var object2 = {
  a: 10
}

let exist = false, exist2 = false;

try {
  exist  = !!object.innerObject.deepObject.value
  exist2 = !!object2.innerObject.deepObject.value
}
catch(e) {
}

console.log(exist);
console.log(exist2);

Comments

0

Try this nice and easy solution:

public hasOwnDeepProperty(obj, path)
{
    for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
    {
        obj = obj[path[i]];
        if (!obj) return false;
    };
    return true;
}

1 Comment

An explanation would be in order.
0

In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:

const assert = require('assert');
assert.deepEqual(testedObject, {
   innerObject:{
      deepObject:{
          value:'Here am I'
      }
   }
});

Comments

0

I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.

function objectHasOwnNestedProperty(obj, keys)
{
  if (!obj || typeof obj !== 'object')
  {
    return false;
  }

  if(typeof keys === 'string')
  {
    keys = keys.split('.');
  }

  if(!Array.isArray(keys))
  {
    return false;
  }

  if(keys.length == 0)
  {
    return Object.keys(obj).length > 0;
  }

  var first_key = keys.shift();

  if(!obj.hasOwnProperty(first_key))
  {
    return false;
  }

  if(keys.length == 0)
  {
    return true;
  }

  return objectHasOwnNestedProperty(obj[first_key],keys);
}

Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
{
    value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
    enumerable: false
});

1 Comment

Who invented the "recursive and happy flow coding strategy"?
0

The solution is a simple one-liner:

console.log((((object||{}).innerObject||{}).deepObject||{}).value)

if one of the inner objects does not exist the output will be "undefined".

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.