JavaScript get nested property without breaking [duplicate] - javascript

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Access Javascript nested objects safely
(14 answers)
Closed 5 years ago.
I am receiving a deeply nested object. I don't know which properties will be present. If I want to get the image, I can't just do this
data.opposite.info.logo.images.s.resized_urls.s,
Imagine this comes from a system where user can leave the logo empty, then my code will break. Do I need to check the existence of properties like this?
if(data.opposite){
if(data.opposite.info)
if(data.opposite.info.images)
//...etc
}

Use javascript's try { ... } catch(e) { ... } block.
try {
url = data.opposite.info.logo.images.s.resized_urls.s; }
catch(e) {
url = ''
}
Also loadash.js has a get method that can help you with this
var url = _.get(data, 'opposite.info.logo.images.s.resized_urls.s', '');
this will traverse deep into the object if the path exists and return the 's'. If it is not there, it returns the last argument which is '' in this case.

I'm trying to get value by scan object: jsfiddle
function getObjByProperty(obj, propertyName) {
if (obj instanceof Object) {
if (obj.hasOwnProperty(propertyName)) {
return obj[propertyName];
}
for (var val in obj) {
if (obj.hasOwnProperty(val)) {
var result = getObjByProperty(obj[val], propertyName);
if (result != null) {
return result;
}
}
}
} else {
return null;
}
}
var images = getObjByProperty(data, 'images');
if (images) {
console.log(images);
}

Related

javascript ignore-if-null operator? [duplicate]

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 6 years ago.
In javascript I have a lot of code like this.
if (ctrl && ctrl.main && ctrl.main.user) {
SetTheme(ctrl.main.user.theme);
}
which is annoyingly long. In other, languages you can do simply
SetTheme(ctrl?.main?.user?.theme);
Is there any way to do that in javascript?
I tried,
function safeGet(a,b) { return a ? a[b] : null; }
and
SetTheme(safeGet(safeGet(safeGet(ctrl, 'main'), 'user'), 'theme'));
But that's not very readable.
The correct short cut could be
if (((ctrl || {}).main || {}).user) { // ...
Or you could use an array as path, or a dot separated string as path and check aginst existence and return the value.
function getValue(object, path) {
return path.split('.').reduce(function (o, k) {
return (o || {})[k];
}, object);
}
var ctrl = { main: { user: { theme: 42 } } };
console.log(getValue(ctrl, "main.user.theme"));
You could create a generic function to do this by passing it the string representing the path to the nested property you want:
function getValue(object, prop, /*optional*/ valIfUndefined) {
var propsArray = prop.split(".");
while(propsArray.length > 0) {
var currentProp = propsArray.shift();
if (object.hasOwnProperty(currentProp)) {
object = object[currentProp];
} else {
if (valIfUndefined) {
return valIfUndefined;
} else {
return undefined;
}
}
}
return object;
}
Then use this on any object like:
// This will return null if any object in the path doesn't exist
if (getValue(ctrl, 'main.user', null)) {
// do something
}

Return undefined on Array.map [duplicate]

This question already has answers here:
Why does JavaScript map function return undefined?
(13 answers)
Closed 1 year ago.
I don't want this map to return undefined how can i do that ?
var onCompareSelectedClick = function () {
var talentProfileInfoForAppliedResources = appliedResourcesEntries.map(function(res) {
console.log(res);
if(res.compareSelected == true) {
return data.getTalentProfileInfo(res.RES.RES_ID);
}
});
console.log(talentProfileInfoForAppliedResources);
this.openCompareTPDlg(talentProfileInfoForAppliedResources);
}.bind(this);
Just add else statement inside map method returning needed value, like:
if(res.compareSelected == true) {
return data.getTalentProfileInfo(res.RES.RES_ID);
} else {
return 'default_value';
}
TL;DR
Use the Array.filter method after Array.map to remove undefined elements in the new array.
Expanding on #Bloomca's answer:
As stated in the documentation provided here.
The map() method creates a new array with the results of calling a provided function on every element in this array.
Hence the reason why your new array contains undefined elements is because you are not explicitly calling return within the function on some elements that are called using the provided function. In Javascript, not explicitly calling return will nevertheless return undefined.
For example, in the following method newArray will be set to the logged result:
[ undefined, 2, 3 ]
newArray = [1,2,3].map(function(elem) { if (elem > 1) return elem })
console.log(newArray)
This is why the answer provided above will no longer result in undefined elements within the new array. The conditional will resolve if the condition res.compareSelected == true is not true to the return statement within the else block (note that you could simply remove the true here and simply put res.compareSelected which would be better practice).
Based on your question you may find using the Array.filter method to return an Array without the undefined values. And with only the values on which you have called the function data.getTalentProfileInfo(res.RES.RES_ID).
You could do this in the following manner:
var onCompareSelectedClick = function () {
var arr = appliedResourcesEntries.map(function(res) {
if(res.compareSelected == true) {
return data.getTalentProfileInfo(res.RES.RES_ID);
}
});
var talentProfileInfoForAppliedResources = arr.filter(function(elem) {
return elem;
});
console.log(talentProfileInfoForAppliedResources);
this.openCompareTPDlg(talentProfileInfoForAppliedResources);
}.bind(this);
You can read about the Array.filter method here.

Possible to ignore Cannot read property '0' of undefined? [duplicate]

This question already has answers here:
How to avoid 'cannot read property of undefined' errors?
(18 answers)
Closed 2 years ago.
I am creating a personal script that in some instances gets the error:
Cannot read property '0' of undefined
I have something like this
item["OfferSummary"][0]["LowestUsedPrice"][0]["FormattedPrice"]
Is it possible to completely ignore/override this error so that it just prints n/a or -- in that scenario?
You can use try and catch to perform error handling.
You can use a boilerplate function to do so:
function get(obj, property) {
if (Array.isArray(property)) {
var current = obj;
for (var i = 0, l = property.length; i < l; ++i) {
if (Object(current) === current) current = current[property[i]];
else {
current = undefined;
break;
}
}
return current;
}
if (Object(obj) === obj) return obj[property];
}
Pass either a string or an array to get to find the property -- if not found, undefined will be returned.
Example:
get(window, ['location', 'href']); // "http://stackoverflow.com..."
get(Number, 'MAX_VALUE'); // 1.7976931348623157e+308
Even if you can use try and catch I wouldn't do that, I prefer avoid errors at all, so you'd just need to check the object you're reading:
if(item && item["OfferSummary"].length && item["OfferSummary"][0]["LowestUsedPrice"].length) {
//then do whatever
}
if you know that item is always defined you can avoid to check it in the if.
Similar to Qantas' answer, but using an in test. Always expects the property list to be an array, I can't see the point of using this to get a single property so no concession for that case:
function get2(obj, prop) {
for (var i=0, iLen=prop.length - 1; i<iLen; i++) {
if (typeof obj[prop[i]] == 'object') {
obj = obj[prop[i]];
} else {
// Property not found, return undefined (or other suitable value)
return;
}
}
return obj[prop[i]];
}
var foo = {foo:{bar:{meh:'meh!'}}};
var fum = {meh:'meh!'};
console.log(get2(foo,['foo','bar','meh'])); // meh!
console.log(get2(fum,['meh'])); // meh!
console.log(get2(Number,['MAX_VALUE'])); // 1.7976931348623157e+308
console.log(get2(Object,['prototype','toString'])); // function toString() { ... }
Edit
Per Qantas' comment, the test has been updated.

What's the simplest approach to check existence of deeply-nested object property in JavaScript? [duplicate]

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 8 years ago.
I have to check deeply-nested object property such as YAHOO.Foo.Bar.xyz.
The code I'm currently using is
if (YAHOO && YAHOO.Foo && YAHOO.Foo.Bar && YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
}
This works, but looks clumsy.
Is there any better way to check such deeply nested property?
If you expect YAHOO.Foo.Bar to be a valid object, but want to make your code bulletproof just in case it isn't, then it can be cleanest to just put a try catch around it and let one error handler catch any missing segment. Then, you can just use one if condition instead of four that will detect if the terminal property exists and a catch handler to catch things if the intermediate objects don't exist:
try {
if (YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// handle error here
}
or, depending upon how your code works, it might even just be this:
try {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// do whatever you want to do when YAHOO.Foo.Bar.xyz doesn't exist
}
I particularly use these when dealing with foreign input that is supposed to be of a particular format, but invalid input is a possibility that I want to catch and handle myself rather than just letting an exception propagate upwards.
In general, some javascript developers under-use try/catch. I find that I can sometimes replace 5-10 if statements checking input with a single try/catch around a larger function block and make the code a lot simpler and more readable at the same time. Obviously, when this is appropriate depends upon the particular code, but it's definitely worth considering.
FYI, if the usual operation is to not throw an exception with the try/catch, it can be a lot faster than a bunch of if statements too.
If you don't want to use the exception handler, you can create a function to test any arbitrary path for you:
function checkPath(base, path) {
var current = base;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
Example usage:
var a = {b: {c: {d: 5}}};
if (checkPath(a, "b.c.d")) {
// a.b.c.d exists and can be safely accessed
}
var _ = {};
var x = ((YAHOO.Foo || _).Bar || _).xyz;
Consider this utility function:
function defined(ref, strNames) {
var name;
var arrNames = strNames.split('.');
while (name = arrNames.shift()) {
if (!ref.hasOwnProperty(name)) return false;
ref = ref[name];
}
return true;
}
Usage:
if (defined(YAHOO, 'Foo.Bar.xyz')) {
// operate on YAHOO.Foo.Bar.xyz
}
Live demo: http://jsfiddle.net/DWefK/5/
If you need to check the correctness of the path, rather than the existance of the "xyz" member on the "YAHOO.Foo.Bar" object, it will probably be easiest to wrap the call in a try catch:
var xyz;
try {
xyz = YAHOO.Foo.Bar.xyz;
} catch (e) {
// fail;
};
Alternately, you can do some string-kong-fu-magicTM:
function checkExists (key, obj) {
obj = obj || window;
key = key.split(".");
if (typeof obj !== "object") {
return false;
}
while (key.length && (obj = obj[key.shift()]) && typeof obj == "object" && obj !== null) ;
return (!key.length && typeof obj !== "undefined");
}
The use as follows:
if (checkExists("YAHOO.Foo.Bar.xyz")) {
// Woo!
};
This problem is solved quite beautifully by coffeescript (which compiles down to javascript):
if YAHOO.Foo?.Bar?.xyz
// operate on YAHOO.Foo.Bar.xyz
use a try catch.
a={
b:{}
};
//a.b.c.d?true:false; Errors and stops the program.
try{
a.b.c.d;
}
catch(e){
console.log(e);//Log the error
console.log(a.b);//This will run
}
I actually voted to close the question as duplicate of javascript convert dotnotation string into objects.
However, I guess it's a different topic, but the answer there might still be helpful if you don't want to try-catch all the time.

How do you know if an object is JSON in javascript? [duplicate]

This question already has answers here:
How to check if it's a string or json [duplicate]
(5 answers)
Closed 8 years ago.
How do I know if a variable is JSON or if it is something else? Is there a JQuery function or something I can use to figure this out?
Based on your comments, it sounds like you don't want to know whether a string is valid JSON, but rather whether an object could be successfully encoded as JSON (e.g. doesn't contain any Date objects, instances of user-defined classes, etc.).
There are two approaches here: try to analyze the object and its "children" (watch out for recursive objects) or suck-it-and-see. If you have a JSON encoder on hand (JSON.stringify in recent browsers or a plugin such as jquery-json), the latter is probably the simpler and more robust approach:
function canJSON(value) {
try {
JSON.stringify(value);
return true;
} catch (ex) {
return false;
}
}
Analyzing an object directly requires that you be able to tell whether it is a "plain" object (i.e. created using an object literal or new Object()), which in turn requires you be able to get its prototype, which isn't always straightforward. I've found the following code to work in IE7, FF3, Opera 10, Safari 4, and Chrome (and quite likely other versions of those browsers, which I simply haven't tested).
var getPrototypeOf;
if (Object.getPrototypeOf) {
getPrototypeOf = Object.getPrototypeOf;
} else if (typeof ({}).__proto__ === "object") {
getPrototypeOf = function(object) {
return object.__proto__;
}
} else {
getPrototypeOf = function(object) {
var constructor = object.constructor;
if (Object.prototype.hasOwnProperty.call(object, "constructor")) {
var oldConstructor = constructor; // save modified value
if (!(delete object.constructor)) { // attempt to "unmask" real constructor
return null; // no mask
}
constructor = object.constructor; // obtain reference to real constructor
object.constructor = oldConstructor; // restore modified value
}
return constructor ? constructor.prototype : null;
}
}
// jQuery.isPlainObject() returns false in IE for (new Object())
function isPlainObject(value) {
if (typeof value !== "object" || value === null) {
return false;
}
var proto = getPrototypeOf(value);
// the prototype of simple objects is an object whose prototype is null
return proto !== null && getPrototypeOf(proto) === null;
}
var serializablePrimitives = { "boolean" : true, "number" : true, "string" : true }
function isSerializable(value) {
if (serializablePrimitives[typeof value] || value === null) {
return true;
}
if (value instanceof Array) {
var length = value.length;
for (var i = 0; i < length; i++) {
if (!isSerializable(value[i])) {
return false;
}
}
return true;
}
if (isPlainObject(value)) {
for (var key in value) {
if (!isSerializable(value[key])) {
return false;
}
}
return true;
}
return false;
}
So yeah… I'd recommend the try/catch approach. ;-)
function isJSON(data) {
var isJson = false
try {
// this works with JSON string and JSON object, not sure about others
var json = $.parseJSON(data);
isJson = typeof json === 'object' ;
} catch (ex) {
console.error('data is not JSON');
}
return isJson;
}
You can use [json2.js] from Douglas Crockfords JSON Github site to parse it.
JSON is an encoding method not an internal variable type.
You might load in some text that is JSON encoded that javascript then uses to populate your variables. Or you might export a string that contains a JSON encoded dataset.
The only testing I've done is to check for a string, with and without double quotes, and this passes that test. http://forum.jquery.com/topic/isjson-str
Edit:
It looks like the latest Prototype has a new implementation similar to the one linked above. http://prototypejs.org/assets/2010/10/12/prototype.js
function isJSON() {
var str = this;
if (str.blank()) return false;
str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '#');
str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
return (/^[\],:{}\s]*$/).test(str);
}

Categories

Resources