Check property exist in object or not in JavaScript? - javascript

I have one problem statement.
Implement a function propertyExists(obj, path) that takes in an object and a path (string) as arguments and returns ‘False’ if the property doesn’t exist on that object or is null, else returns the value of the property.
And here is solution.
function propertyExists(obj,path) {
// Write logic here
let result = obj.hasOwnProperty(path);
if(result)
{
return (obj.path);
}
else
{
return result;
}
}
Is this correct way of doing it?

Multiple issues:
the first name of the function should represent what it is doing,
Path as variable name is vague but propertyName as a variable is clear.
what you should do is either:
write function called, "getValue" it returns value if exist or null
function getValue(obj,propertyName) {
if(!obj) { // if object not exist
return null;
}
return obj[propertyName];
}
write function called, "propertyExists" should return true if exist else false.
function propertyExists(obj,propertyName) {
if(!obj) { // if object not exist
return false;
}
return obj.hasOwnProperty(propertyName);
}

function propertyExists(obj,path) {
// Write logic here
for(i in path){
if(obj.hasOwnProperty(path[i]) === true){
obj = obj[path[i]];
}else{
return false;
}
}
return obj;
}

object->obj,propertyname->path
function propertyExist(obj, path){
if (obj.hasOwnProperty(path)) return obj[path];
else return false;
};

Here, as far as I can understand, the objects could be nested also.
Let's take obj as {"a":{"b":"dadsa"}} and path as ab
Now, the the solution which you have posted will not work. Ideally, it should return dadsa, but it will return false.
Try the below code, it will work for nested objects also.
function propertyExists(obj,path) {
// Write logic here
var val=obj;
for(a of path){
val=val[a];
if(!val)
return false;
}
return val;
}

function propertyExists(obj,path) {
var val = obj;
for (a of path) {
val = val[a];
if (!val){
return false;
}
return val;
}

Related

Test if Tree contains Object

I need a function to check if an object exists in a tree.
I recursively run through the tree and use lodash to check for equality of objects:
var objectInResultList = function (obj, list) {
list.forEach(function (item) {
if (_.isEqual(item, obj) === true) {
return true
}
else if (item.children.length > 0) {
return objectInResultList(obj, item.children);
}
});
return false;
};
var item = {"name":"Enterprise1.1","description": "testTest","children":[]};
var resultList = [{"name":"Enterprise1.1","description": "testTest","children":[{"name":"Enterprise1.1","description": "testTest","children":[]}]}];
var ret = objectInResultList(item, resultList);
alert(ret);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.2.1/lodash.min.js"></script>
The function should return true if the item is in the list and otherwise return false, but currently it always returns false.
Can someone find my problem?
JSFiddle
You are missing a couple of things.
A minor thing - your predicate is not returning false if the objects don't match and it has no children.
You are returning true/false from the predicate, but aren't using it anywhere so objectInResultList always returns false.
Rather than using forEach, it is easier to use find or findIndex, and the use the result of that to determine what to return from objectInResultList. For example if find returns undefined because of no match, then you return false.
In this case, you are using a tree with multiple lists, so using find makes more sense than findIndex.
var objectInResultList = function (obj, list) {
// call find to get the matching object
var match = list.find(function (item) {
if (_.isEqual(item, obj) === true) {
return true;
}
else if (item.children.length > 0) {
return objectInResultList(obj, item.children);
}
else {
return false;
}
});
// if match is undefined return false. If we found a match, return true
return !_.isUndefined(match);
};
var item = {"name":"Enterprise1.1","description": "testTest","children":[]};
var resultList = [{"name":"Enterprise1.1","description": "testTest","children":[{"name":"Enterprise1.1","description": "testTest","children":[]}]}];
var ret = objectInResultList(item, resultList);
alert(ret);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.2.1/lodash.min.js"></script>
forEach() has no return-value and it ignores any returned value. So returning true or false in the search-function is useless.
You should use a better iterator-function.
var objectInResultList = function (obj, list) {
function search(item){
if(item == null) return false;
if(_.isArray(item)) return _.some(item, search);
if(_.isEqual(item, obj)) return true;
return search(item.children);
}
return search(list);
};

Object has-property-deep check in JavaScript

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')
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'));
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
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.'
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
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);
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;
}
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'
}
}
});
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
});

Function call with additional sub functions

I'm asking myself if it's possible to call a function in js, while having additional subfunction inside it
fn(s);
fn.subfn(s);
for example to make utils like this
var s = "123";
string(s) // true
string.blank(s) // false
I think it's possible like this:
function string(s) {
if(s) return typeof(s) === "string";
return {
blank: function(s) {
return s.trim().length === 0;
}
}
}
but every time i call string(s) i'm redefining blank fn, with possible poor performances and poor code, or i'm wrong?
Thanks.
Functions are just objects, so yes, you can just add properties to them:
function string(s) {
return typeof(s) === "string";
}
string.blank = function(s) {
return s.trim().length === 0;
}
This would allow you to make the calls
string(s);
string.blank(s);
just as shown in your example.
Comments to your code:
The function you defined returns an object when you call string, so you would require to call the function as
string().blank(s);
which would be different form the example you showed at the beginning.
You can create a Thing() class and instantiate "thing" objects to prevent redefining functions. (Thing() instead of string() to prevent any sort of collision.)
function Thing(s) {
return {
isString: function() {
return typeof(s) === "string";
},
isBlank: function() {
return s.trim().length === 0;
}
};
}
var t = new Thing("123");
t.isString() // true
t.isBlank() // false
http://jsfiddle.net/KKrsa/
You could try something like this (untested):
function string(s) {
if(s) return typeof(s) === "string";
}
string.blank = function(s) {
return s.trim().length === 0;
}
You might run into issues using "string" for the name of your function, though, because it may clash with the existing String object.

Function with forEach returns undefined even with return statement

I'm just making a function for checking a value of something in my object array, but for some reason it keeps returning undefined. Why is that?
Demo: http://jsfiddle.net/cNYwz/1/
var data = [{
"Key": "1111-1111-1111",
"Email": "test#test.com"
}, {
"Key": "2222-2222-2222",
"Email": "test#boo.com"
}];
function getByKey(key) {
data.forEach(function (i, val) {
if (data[val].Key === key) {
return data[val].Key;
} else {
return "Couldn't find";
}
});
}
var asd = getByKey('1111-1111-1111');
console.log(asd);
In your function, you're returning from the function passed to forEach, not from getByKey.
You could adapt it like this :
function getByKey(key) {
var found = null;
data.forEach(function (val) {
if (val.Key === key) {
found = val;
}
});
return found;
}
But this would iterate over all elements, even if the item is immediately found. That's why you'd better use a simple for loop :
function getByKey(key) {
for (var i=0; i<data.length; i++) {
if (data[i].Key === key) {
return data[i];
}
}
}
Note that I also adapted your code to return the value, not the key. I suppose that was the intent. You might also have been confused with another iteration function : the first argument passed to the callback you give to forEach is the element of the array.
Your function getByKey has no return statement. The two returns are for the anonymous function used by forEach.
You're not returning anything to the outer scope, try this alternative:
function getByKey(key) {
var result = data.filter(function (i, val) {
return data[val].Key == key;
});
return result.length ? result : 'Not found';
}
Try storing the positive result as a variable, and then returning that variable (or a "Couldn't find" in case nothing is written) at the end of the function after the forEach loop.
function getByKey(key) {
var result;
data.forEach(function (val, i) {
if (data[val].Key === key) {
result = data[val].Key;
}
});
return result || "Couldn't find";
}
In addition to the ideas in the other answers, you're better off using Array.prototype.some, rather than forEach. That will let you stop when you find the first match:
function getByKey(key) {
var found = null;
data.some(function (val) {
if (val.Key === key) {
found = val;
return true; //stop iterating
}
});
return found;
}
You might also consider using filter, which can return an array containing just the objects where key matches:
function filter_array_by_key(key){
return data.filter(function(v){
return v.Key===key;
};
}
To get the first matching object, you can then use filter_array_by_key(key)[0], which will yield undefined if there was no match.

can't convert undefined into an object

I am getting the error "Can't convert undefined to object on the line return hasOwnProperty(prop); and I simply cannot figure out where the problem lies. I can post more of the code if necessary.
getCardProperty : function (card, prop, def) {
if (typeof def === "undefined") {
def = null;
}
// json synckolab object
if (card.synckolab) {
if (card.hasOwnProperty(prop)) // TODO better check for undefined?
{
return hasOwnProperty(prop);
}
return null;
}
hasOwnProperty(prop) doesn't exist - you need to qualify it with an object name. Just change it to card.hasOwnProperty(prop).
You can simplify it further:
if (card.synckolab) {
return card.hasOwnProperty(prop) || null;
}
This will return true or null. Or you can simplify even further:
if (card.synckolab) {
return card.hasOwnProperty(prop);
}
This will return true or false.

Categories

Resources