does object/array exist in javascript - javascript

I'm trying to put content from RSS feed - problem is every RSS feed has different formats for images, content etc.
I'm trying to see if certain object exists in javascript or jquery:
item.mediaGroups[0].contents[0].url
How can I check it in an if statement? I keep getting for feeds without this structure:
Uncaught TypeError: Cannot read property '0' of undefined
Also tried:
if (typeof item.mediaGroups[0].contents[0].url === "undefined")
but I keep getting the same error.
thanks!

There is no "simple" built in way to do this sort of in depth checking. The reasoning is simple - most of the time you know the type of the objects you're working against.
You can do:
if (typeof item !== "undefined" &&
typeof item.mediaGroups !== "undefined" &&
typeof item.mediaGroups[0] !== "undefined" &&
typeof item.megiaGroups[0].contents !== "undefined" &&
typeof item.megiaGroups[0].contents[0] !== "undefined" &&
typeof item.mediaGroups[0].contents[0].url !== "undefined"){
When you type all that you might want to consider your data structures, since this really is not a situation you should be in to begin with :)
(hint, you can skip the typeof on all but the first, but I think typeof is a good clarification here).
The real question is this:
Why are you not sure what the structure of your data is?
If you are querying data (for example XML in an RSS feed) there are effective ways to do so with XPATH or query selectors. Object property access is built for objects where what you're querying is a document. Sure, it's possible with a bunch of ugly checks just like you can hammer a nail in with a heavy screwdriver.
You can see this question in Stack Overflow on how to use DOM methods to parse XML.

If you're uncertain about the exisence of properties, try this helper function:
function getProperty(root) {
var l = arguments.length, i, undefined;
for( i=1; i<l; i++) {
if( typeof root[arguments[i]] == "undefined") return undefined;
root = root[arguments[i]];
}
return root;
}
You can then call it like this:
var url = getProperty(item,'mediaGroups',0,'contents',0,'url');
As a more "haxy" way, you can try this:
try {url = item.mediaGroups[0].contents[0].url;}
catch(e) {url = undefined;}

I would check the length of both arrays in this case to be sure - before assuming there are objects defined at index 0
item.mediaGroups.length > 0
and
item.mediaGroups[0].contents.length > 0
As the outer check you can also throw in a
if(item.mediaGroups){
}

How about 'optional chaining' (described in ES2021 spec and already implemented in all browsers except three) ?
from MDN:
The optional chaining operator provides a way to simplify accessing
values through connected objects when it's possible that a reference
or function may be undefined or null.
The optional chaining ?. stops the evaluation if the value before ?. is undefined or null and returns undefined so it is giving us a way to handle the possibly undefined/nullsish values
item?.mediaGroups[0]?.contents[0]?.url // will evaluates to undefined if either of those is undefined.

item.mediaGroups[0].contents is undefined, you have to check for it.
if(item.mediaGroups && item.mediaGroups[0].contents) {
return item.mediaGroups[0].contents[0].url;
}

It's not a solution with if-statements (as requested), but you can use exceptions to achieve similar functionality. Something like this:
function throwOrReturn(thing){
if(typeof thing === 'undefined'){
throw "Didn't find it..."
}else{
return thing
}
}
// The unknown thing.
var a = {
b1: {
},
b2: {
c: 'lookingFor'
}
}
var c
// Test our different paths.
try{
// First guess.
c = throwOrReturn(a.b1.c.d)+" - a.b1.c.d"
}catch(error){
try{
// Second guess.
c = throwOrReturn(a.b[45][34].c)+" - a.b[45][34].c"
}catch(error){
try{
// Third guess.
c = throwOrReturn(a.b2.c)+" - a.b2.c"
}catch(error){
// Try more guesses, or give up.
c = "notFound"
}
}
}
console.log("c:", c) // Logs: "c: lookingFor - a.b2.c"
It ain't pretty, but it's an alternative worth to mention.

Related

Returning false if variable doesn't exist

I have some code structured like this but with a bunch of variables with paths of various depths within dict that may or may not exist:
var dict = {
'test1': 'test',
'test2': ['testa', 'testb'],
}
var test_path1 = dict['test2']['testa'] ? test_path1: false
var test_path2 = dict['test3']['testz'] ? test_path2: false
console.log(test_path2)
Basically my program creates a bunch of arrays that it saves within user_dict depending on user input. Later I need to process dict and check some of the variables to see their values, or whether or not they exist.
I can't even get to that point though, since defining test_path2 returns "cannot read property testz of undefined."
I thought using ? test_path2: false would work, but I still get that same error.
Someone suggested using optional chaining, but that doesn't seem like a good solution since some of my variables are located within 4-5 nested objects/arrays, each of which may or may not exist.
What's the best way to handle this? Is there an error with my syntax or am I approaching the problem the wrong way? All I need is for test_path1 and test_path2 to return false if it doesn't exist.
Arguably, the best way to handle this (while keeping legacy compatibility) is using get from lodash:
import { get } from 'lodash';
const result = get(dict, ['test2', 'testa']) || false;
// or
const result = get(dict, 'test2.testa') || false;
Note: to only import get (and nothing else) from lodash, use lodash-es instead of lodash. In other words, by using lodash-es you enable tree-shaking lodash at build step.
or you could simply check each level:
const result = dict && dict.test2 && dict.test2.testa || false;
If legacy compatibility is not an issue, you could use optional chaining, as suggested in Noriller's answer.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
optional chaining can and will work even on deep nested objects
to top it off, you can use the nullish coalescing to return a "default" (fale in your case)
var test_path1 = dict['test2']?.['testa'] ?? false
this will return the value or false if it is undefined
You can use some modern javascript features. Try Optional Chaining:
var dict = {
'test1': 'test',
'test2': { 'testa': 'testb' },
}
var test_path1 = dict['test2']?.['testa'] || false
var test_path2 = dict['test3']?.['testz'] || false
console.log(test_path1, test_path2)
If you want to write more optimistic code you could also use a try catch at some point in the code.
try {
return dict.test1.test2
} catch (e) {
if (e instanceof TypeError)
return false
throw e
}
This prevents the need for writing code that does null checks.

Concise way to null check a deeply nested object?

JavaScript for browser
I need to test that one deeply embedded property is not null and if this condition is true, then to change its property. But any its parent can be null also. Therefore I am to check each item in the chain... Therefore I write such ugly code:
if(window[rootNamespace].vm.tech &&
window[rootNamespace].vm.tech.currentWorkType &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet){
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion
.currentWorkSection.currentStageSet.selectedEntity = item;
}
Is there a shorter method of checking?
(I was the original poster proposing the try-catch method, but based on the discussion on that post you were worried about performance. Here's an alternate approach.)
You can use prototype methods to implement a safe method of accessing subproperties. Here is a method which can safely test for the existence of a nested property:
// Indicates whether an object has the indicated nested subproperty, which may be specified with chained dot notation
// or as separate string arguments.
Object.prototype.hasSubproperty = function() {
if (arguments.length == 0 || typeof(arguments[0]) != 'string') return false;
var properties = arguments[0].indexOf('.') > -1 ? arguments[0].split('.') : arguments;
var current = this;
for(var x = 0; x < properties.length; x++) {
current = current[properties[x]];
if ((typeof current) == 'undefined') return false;
}
return true;
};
A full set of methods can be found here, with sample code.
Timings can be run here, and indicate that using the try-catch method may run a couple of orders of magnitude slower than your original approach when errors are thrown, but is otherwise quite fast. The prototype methods are more generic and can lead to a more declarative style, while offering much better performance than try-catch misses, but obviously not quite as good as hand-crafting if statements each time and/or try-catch without a miss.
I've also blogged about this approach.
Syntax wise I don't think so, but I recommend refactoring at least.
var getCurrentStageSet = function(window){
return window[rootNamespace].vm.tech &&
window[rootNamespace].vm.tech.currentWorkType &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet
}
var setSelectedEntity = function(currentStageSet, item){
currentStageSet.selectedEntity = item;
}
By abstracting this logic your actual set of the property will be more readable, and reusable:
var currentStageSet = getCurrentStageSet(window);
if (currentStageSet){
setSelectedEntity(currentStageSet, item);
}
For such a trivial, self-contained piece of code, it's probably not unreasonable to just catch and ignore the error (possibly log) e.g.
try {
if (window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet) {
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet = item;
}
} catch (e) {
// log the error but continue
}
Not sure what else could really go wrong in this type of check, alternatively you could catch a TypeError specifically but not sure it would really matter all that much.
I generally wouldn't recommend catch all's but in this case it seems self contained enough to not be a huge risk.
Anything beyond that requires effort e.g. building an object decorator or a fluent interface type solution, seems overkill to me though.
You can create some variables to get code more readable
var tech = window[rootNamespace].vm.tech;
var workType, curVariant, curVer, curWorkSection;
if(tech){
workType = tech.currentWorkType
}
if(workType){
curVariant = workType.currentVariant;
}
if(curVariant){
curVer =curVariant.currentVersion;
}
if(curVer){
curWorkSection = curVer.currentWorkSection;
}
if(curWorkSection && curWorkSection.currentStageSet){
curWorkSection.currentStageSet.selectedEntity = item;
}
This is the most compact syntax possible in basic JavaScript. It avoids all the null-checking by using error-trapping instead. None of the other answers are as compact because the language is simply missing the feature you're after from C#.
Apparently, I'm being down-voted by the authors of the other, much less compact answers, but this is nevertheless the only single-line answer. Note that other approaches listed here have you creating multiple functions, even. :| If you want compact, this is it.
try { window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion
.currentWorkSection.currentStageSet.selectedEntity = item; } catch (err) {}

Unexpected token: u JSON.parse() issue

I have read online that the unexpected token u issue can come from using JSON.parse(). On my iPhone 5 there is no problem, but on my Nexus 7 I get this sequence of errors:
View large
I realize this is a duplicate, but I am not sure how to solve this for my specific problem. Here is where I implement JSON.parse()
$scope.fav = [];
if ($scope.fav !== 'undefined') {
$scope.fav = JSON.parse(localStorage["fav"]);
}
Base on your updated question the if condition does not make sense, because you set $scope.fav to [] right before, so it can never be "undefined".
Most likely you want to have your test that way:
if (typeof localStorage["fav"] !== "undefined") {
$scope.fav = JSON.parse(localStorage["fav"]);
}
As i don't know if there is a situation where localStorage["fav"] could contain the string "undefined" you probably also need test for this.
if (typeof localStorage["fav"] !== "undefined"
&& localStorage["fav"] !== "undefined") {
$scope.fav = JSON.parse(localStorage["fav"]);
}
One way to avoid the error (not really fixing it, but at least won't break):
$scope.fav = JSON.parse(localStorage["fav"] || '[]');
You're getting that error because localStorage["fav"] is undefined.
Try this and you'll understand all by yourself:
var a = undefined;
JSON.parse(a);
Unexpected token: u almost always stems from trying to parse a value that is undefined.
You can guard against that like this:
if (localStorage['fav']) {
$scope.fav = JSON.parse(localStorage['fav'];
}
In my case, the problem was I was getting the value as localStorage.getItem[key] whereas it should have been localStorage.getItem(key).
The rest and normally faced issues have been better explained already by the above answers.

If statement throwing error for nonexistent object

The answer to this question seems like it would be obvious, but I'm always looking to improve my semantics, so bear with me.
I have an array structure with individual items containing X,Y coordinates
var example = new Array();
example.push({x:0,y:0});
In my code I have a set interval that updates my canvas and checks for certain conditions. Including one similar to this
if(example[0].x == other.x && example[0].y == other.y)
{
//do something
}
The issue is that the array is very dynamic, and when the code is first executed the example array is empty. Hence, Chrome throws errors along the lines of "Cannot get property x". To shut up the console, I added a dummy item to the array {x:"~", y:"~"} but it seems really unintuitive. Have I implemented an undesirable data structure? What's a simple way to handle if statements for objects that... don't exist?
Why don't you just check whether the array has elements?
if (example.length && ...)
Or whether the first element is true:
if (example[0] && ...)
if (0 in example
&& example[0].x == other.x && example[0].y == other.y) {
// do something
}
(This works for arbitrary index, not just 0; if you just want to check if the array is non-empty, example.length as shown by melpomene is good.)
You should be able to check on the first-level element (i.e. 'example') - JavaScript usually throws errors like this when you try to access a property of an element that is null or undefined. Like some others have already shown:
if(example[0] && example[0].x === other.x)
The point is though that JavaScript will let you have example[0] and return as you like, but once you try to access that property, you're out of luck:
var example = [];
//undefined
example
//[]
example[0]
//undefined <--- this is a falsy value, will evaluate false in a check
example[0].x
//TypeError: Cannot read property 'x' of undefined

How can I speed up this bit of JSON date parsing?

I am stuck using an AJAX library from about 5 years ago in this project, and it had some issues with parsing dates in JSON. I wound up rewriting its parse function to use a single regex:
return eval('(' + (enableDateParsing ? text.replace(/"(?:\\)?\/Date\((.*?)\)(?:\\)?\/"/g, "new Date($1)") : text) + ')');
This works really well, but I thought I could get a speed up if I used native JSON parsing in IE8 / chrome / ff, so I added this bit:
if (typeof JSON !== 'undefined' && typeof JSON.parse !== 'undefined') {
var nativeJsonDateParseRegex = /\/Date\(.*?\)\//g;
return JSON.parse(text, function (key, value) {
if (AjaxPro.enableDateParsing && typeof value === 'string' && value.match(nativeJsonDateParseRegex))
{
value = new Date(parseInt(value.substr(6)));
}
return value;
});
}
else // revert to eval for ie6/ie7
The reviver callback will execute once for each JSON property returned, so it has to be very fast. During a profile I've seen it's been called 170484 times, but still runs pretty fast (131.237ms). Any ideas on how to make it faster, or is this the best you can do without serious tweaking?
Your code contains a lot of constant conditions, you'll be fine with checking once whether native JSON is supported or not.
Suggestions:
check for native JSPN support at page load, and add the right function accordingly.
Drop the global flag from the regex if you do not need it
Drop regular expressions if possible, if every date always starts with "/Date(", search for it. It's much faster (see benchmark at jsperf.com)
todo: check whether parseInt can be replaced with an other method to get rid of the trailing )/.
If AjaxPro.enableDateParsing is a constant, you can remove if from AjaxPro.jsonParse and and make it a condition like the check for native JSON
Code without RE:
if (typeof JSON !== 'undefined' && typeof JSON.parse !== 'undefined') {
AjaxPro.nativeJsonDateParseRegex = /\/Date\(.*?\)\//g;
AjaxPro.dateFunc = function(key, value) {
if (typeof value === "string" && !value.indexOf("/Date(")) {
return new Date(value.substring(6, value.length-2));
}
return value;
};
AjaxPro.jsonParse = function(text) {
if (AjaxPro.enableDateParsing) {
return JSON.parse(text, AjaxPro.dateFunc);
}
return JSON.parse(text);
};
} else // revert to eval for ie6/ie7
This should be highly optimized. You might want to run some more test on your own in multiple browsers. Maybe checking for a property of a string is faster than checking its type (doubt it), thing like that.
One not so good microoptimization, but still worth giving a try.
Since your substring contains millisecond timestamp only, and no other garbage string.
You can remove the call to parseInt.
You can try typecasting with simple mathematical operation like multiplication with 1.
Might save some time if you are too keen on microoptimizations.
value = new Date(1*(value.substr(6)));
example:
a = "a:3333";
b = a.substring(2);
alert(b*2); // alerts 6666

Categories

Resources