calling object properties in N levels javascript - javascript

I'm using mongodb and ajax calls to retrieve data. When it turns to javascript object, the properties that I use to generate html sometimes don't exist.
Look at this call:
$.ajax({
url: 'api/v1/mention/'+id,
type: "GET",
dataType: "json",
data : {login : "demo"},
success: function(mention) {
display_mention_text(mention.texto);
}
});
In this case i'm calling mention.texto, but could be mention.picture or any properties. Sometimes it is undefined and crashes the app.
This method calls a property from a object and if its undefined , return an empty string.
Some examples for calling this method(the first one is an object, the other are properties):
get_property(mention,"text")
get_property(mention,"user","name")
get_property(mention,"picture")
The method is defined as follows:
function get_property(obj){
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
if (checkNested(obj,args)) {
//what should I do here?
} else{
//the property is undefined and returns ""
"";
};
}
//check if a object has N levels of propertys
function checkNested(obj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
for (var i = 0; i < args.length; i++) {
if (!obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
In the first method get_property, if the property do exist, how do I call that??
I would have the object and his propertys as an array like:
object
params = ["user","name"]
but I can't call like following:
object.["user","name"]

Replace the if statement in the get_property function with the for loop from the checkNested function. Then instead of returning true or false, return the value found or "".
function get_property(obj){
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
// Here's your 'for' loop. The 'if' statement is gone.
for (var i = 0; i < args.length; i++) {
if (!obj.hasOwnProperty(args[i])) {
return ""; // I changed the value of this 'return' statement
}
obj = obj[args[i]];
}
return obj; // I change the value of this 'return' statement
}
Again, all I did was copy paste your own code from one function to the other, and change the values of the return statements.

You can achieve your goal by this way (no need of checkNested function):
//Parameters {obj, prop1, prop2, ... propN}
function get_property(){
var args = Array.prototype.slice.call(arguments),
obj = args.shift(),
prop = args.shift();
if( obj.hasOwnProperty(prop) ){
if( args.length > 0 ){
//Calling 'get_property' with {obj[prop1], prop2, ... propN} , and so on
return get_property.apply(get_property, [obj[prop]].concat(args));
}else{
return obj[prop];
}
}else{
return "";
}
}​
Usage:
var o = {
"a" : {
"b" : 5
}
};
console.log( get_property(o,"c") ); // ""
console.log( get_property(o,"a","b") ); // 5
console.log( get_property(o,"a") ); // {"b":5}

You're just looking for a simple recursive function.
function get_property(obj, prop) {
if (obj[prop]) {
return obj[prop];
}
else {
var end;
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
end = get_property(obj[p], prop);
}
}
if (!end) {
// If we're there, it means there's no property in any nested level
return;
}
else {
return end;
}
}
}
Edit: looks like I misinterpreted your question. See #Engineer's answer instead. Keeping this answer in case someone looks like for something like this.

You're overcomplicating things. If you know the tree of your object, just call:
obj['user']['name'];
If you're expecting undefined properties (and really, that means there's something else wrong):
var prop;
try {
prop = obj.user.name;
}
catch (e) {
prop = null;
}

Related

Make Javascript local variable to global for recursive loops

I have a recursive function which has a local variable.
It calls itself on specific condition.
The local variable needs to be updated, but every call it creates a new local variable specific to the current function scope.
How can i reach the local variable for access all recursive loop and not to create a new one?
Something like __Callee.varname?
The code is:
var addAttribute = function(object,elem)
{
var attributes = [];
// only attribute without values
if ( object instanceof Array )
{
for ( var value in object )
{
attributes.push(object[value]);
}
}
// attribute with values
else if ( object instanceof Object )
{
for ( var key in object )
{
if ( object[key] instanceof Array )
{
addAttribute(object[key],elem);
}
else
{
attributes.push(key+'=\''+object[key]+'\'');
}
}
}
// Only one attribute
else if ( typeof object === 'string' )
{
attributes.push('\''+object+'\'');
}
// Invalid parameter
else
{
console.log('Invalid parameter: '+typeof object);
}
console.log('<'+elem+' '+attributes.join(' ').toString()+' />');
}
I do not want to make variable to global because of using this name in other functions and global scope already.
Use a closure
function fn() {
function recursiveFunction() {
// do something with x
recursiveFunction();
}
var x = 0;
recursiveFunction();
}
The usual thing is to pass it into the function, possibly optionally:
var addAttribute = function(object,elem, attributes) {
attributes = attributes || [];
// ....
Then when calling it recursively, pass in the third argument:
addAttribute(object[key], value, attributes);
Here's a much simplified example demonstrating:
function foo(num, array) {
array = array || [];
array.push(num);
console.log("Pushed " + num + ", array = " + JSON.stringify(array));
if (num < 5) {
foo(num + 1, array);
}
}
foo(1);

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

new Object with prototype function vs regular function which returns an object

The basic idea is to check if it starts with an underscore and if there is split the string and return whatever comes after the underscore. This function will be run many times, but for different strings, it is unlikely i will need to retrieve the information more than once for each stirng.
A simple function which will return an object with the data I need:
var parseElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
var obj = {
isClass: null,
name: ''
}
if(sliced[0] === '_') {
obj.name = sliced[1];
obj.isClass = true;
} else {
obj.name = i;
obj.isClass = false;
}
return obj
}
Called with parseElementName(i);
Object with prototyped function
var parsedElement = function(i) {
this.className =
this.isClass = null;
if(this.setElementName(i))
return true
}
parsedElement.prototype.setElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
if(sliced[0] === '_') {
this.className = sliced[1];
this.isClass = true
} else {
this.className = i;
this.isClass = false
}
}
Called with var parsed_element = new parsedElement();
then parsed_element.className or parsedElement.isClass
Which approach is recommended?
I like the object prototype approach best, but I have a few notes about your code:
Use semicolons at the end of each line
Class names should be capitalized. So it should be ParsedElement
I wouldn't call it className, because it is confusing when it is not a class, I would rename it name
The two ways have different outcomes - that constructor+prototype approach will yield an instance which has a setElementName method. Will you ever need this to change the fields of an existing object? It's a simple parser function, so I would assume no. In that case, you should go with returning the object literal:
function parseElementName(i) {
var isClass = i.charAt(0) == '_';
return {
isClass: isClass,
name = isClass ? i.slice(1) : i
};
}
If you really need that method later, consider #MaxMeier's and #HMR's points.

JS closures and recursion

For example I have this function with a closure:
function getData() {
var status = 0;
var func = function() {
status++
alert(status);
}
return func;
}
Its works correctly and the variable "status" is visible within the closure function.
But if I transfer the closure code into the separate function the closure variable "status" isn't available:
function getData() {
var status = 0;
var func = function() {
myFunction();
}
return func;
}
function myFunction() {
status++
alert(status);
}
Yes, I can send this variable to the function and then return the changed value. But what if I need recursion in "myFunction"?
function getData() {
var status = 0;
var a = function() {
myFunction(status);
}
return a;
}
function myFunction(status) {
if (status == 0) {
status = 1;
// After calling this function again "status" will reset to 0,
// but I want to save current value (status = 1).
data();
}
return status++;
}
var data = getData();
data();
How can I get one instance of my variable "status" for all calls to the closure function.
Thanks!
Firstly, that doesn't work because JS has lexical scopes, not dynamic scopes. More info on Wikipedia
Secondly, if you want to pass in a variable to a function, and allow that function to mutate it, you need to send something that's an instanceof Object.
0 instanceof Object
false
"" instanceof Object
false
[] instanceof Object
true
({}) instanceof Object
true
(function(){}) instanceof Object
true
You can wrap your number into an object and pass it.
Modified example:
function getData() {
var o = {
status: 0
};
var a = function () {
myFunction(o);
}
return a;
}
function myFunction(o) {
console.log(o);
if (o.status == 0) {
o.status = 1;
// After calling this function again "status" will reset to 0,
// but I want to save current value (status = 1).
data();
}
return o.status++;
}
var data = getData();
data();
Output:
Object {status: 0}
Object {status: 1}
http://jsfiddle.net/Dogbert/Tw7nh/
You could replace this line:
myFunction(status);
with:
status = myFunction(status);
or:
JSFIDDLE: http://jsfiddle.net/KHZRr/
function Data(){
var status = 0;
var a = {
getData : function(){
if( status == 0 ){
status = 1;
return "FIRST, status = " + status;
}else{
return "NOT FIRST, status = " + status;
}
}
}
return a;
}
var data = Data();
alert( data.getData() );
alert( data.getData() );
alert( data.getData() );
Is this what you are looking for?
If I transfer the closure code into the separate function the closure variable "status" isn't available.
True, and that won't change due to JavaScript's scoping rules.
Yes, I can send this variable to the function and then return the changed value. But what if I need recursion in "myFunction"?
Nothing changes. You can use recursion from inside the closure as well.
function getData() {
var status = 0;
function data() {
if (status++ == 0) {
// do anything you want
// including recursive calls to `data()`
}
return status;
}
return data;
}
getData()();

Setting variable by string name in javascript?

//window["Fluent"]["Include"]
function setGlobalVariableByName(name,value)
{
var indexes = name.split(".");
var variable = null;
$.each(indexes, function()
{
if (variable == null){
variable = window[this];
}else{
variable = variable[this];
}
});
variable = value;
}
setGlobalVariableByName("Fluent.Include.JqueryPulse",true);
console.log(Fluent.Include.JqueryPulse) // prints false
this doesn't work, obviously. It would work if I just wanted to get the variable's value, but not for setting it.
window["Fluent"]["Include"]["JqueryPulse"] = true;
console.log(Fluent.Include.JqueryPulse) // prints true
how could I achieve something like this without using eval?
I'd need some way to programmatically add array indices to this, I'd guess
The following works, can you suggest a better way to code it in order to make it more DRY?
function setGlobalVariableByName(name,value)
{
var indices = name.split(".");
var parent;
$.each(indices, function(i)
{
if(i==indices.length-1){
if (!parent){
window[this] = value;
}else{
parent[this] = value;
}
}else if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
}
setGlobalVariableByName : function(name, value)
{
var indices = name.split(".");
var last = indices.pop();
var parent;
$.each(indices, function(i)
{
if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
if (!parent){
window[last] = value;
}else{
parent[last] = value;
}
}
You need to call
variable[this] = value
somehow. So you need to break the loop of the splited string before reching the last name, and then assign the value.
Ultimatively you need to call:
variable = window['Fluent']['Include']; // build this in a loop
variable['JqueryPulse'] = someValue; // then call this
Ultimately you're just building an object chain and setting the final item in the chain to a value. Also, I would add a check to ensure that items which are already objects do not get overwritten so that their existing properties don't get lost:
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
$.each( names, function()
{
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[this] !== 'object' || currentOp[this] === null )
{
currentOp[this] = {};
}
//move the reference for the next iteration
currentOp = currentOp[this];
} );
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );
However, I would do it without using jQuery, even if you have jQuery available on the page. There is no need for the overhead of executing a function for each index.
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
indexCount,
currentIndex,
currentName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
indexCount = names.length;
for( currentIndex = 0; currentIndex < indexCount; currentIndex += 1 )
{
currentName = names[currentIndex];
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[currentName] !== 'object' || currentOp[currentName] === null )
{
currentOp[currentName] = {};
}
//move the reference for the next iteration
currentOp = currentOp[currentName];
}
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );

Categories

Resources