I have the following chaining operator
const age = data.student?.age ? data.student.age: '';
this works fine in my local machine but seems to have problem in another machine. on further investigation i could understand that node js lower version(i suppose version below 12) doesn't support chaining operator. I understand that i can replace this with if condition like below but would like to know suggestions on what is the best possible alternative for this.
function checkAge(data){
if(data && data.student && data.student.age) {
return data.student.age;
} else {
return '';
}
}
const age = checkAge(data);
There is no need for code change. You only need to modify the target option in your TypeScript configuration and set it to anything ES2019 or below. Then you can use optional chaining in your TypeScript code and the compiler will produce the equivalent code.
The TypeScript code
const foo = a?.b?.c;
becomes
"use strict";
var _a;
const foo = (_a = a === null || a === void 0 ? void 0 : a.b) === null || _a === void 0 ? void 0 : _a.c;
when compiled: Playground Link
If the problem is readability, you could probably try object destructing.
So, your assigment would look something like this:
const {
student: {
age = ''
} = {}
} = data
Assuming declaring age as const, not to polluting scope with intermediate variables, and returning a number even for '0' and empty string in case of undefined are all a must, shorter option that comes to my mind would be following:
const age = (d=>(isNaN(d=(d.student||{}).age)?'':d))(data);
For less strict approaches, cleaner solution would be:
const age = (data.student || {}).age || "";
// In this case, though, numeric 0 would also be returned as empty string.
On the other hand, if you need to do this more than a few times, I would recommend to implement a handy picking function like:
const pick = (target, path)=>path.split(".")
.reduce(
(acc,key)=>acc&&acc[key]
,target
)
;
// And then...
const age = pick(data, 'student.age');
For picking approach it would be worth to reduce the number of
function calls performed by pick() function. I just used reduce for
the sake of brevity and simplicity.
Related
I have an object that I am creating that could potentially have undefined properties.
Is there a more concise way to set the property than what I am doing below:
var ruleObj = {
factor: (ruleArray[2] ? ruleArray[2].trim() : null),
resultElseTarget: (ruleArray[10] ? ruleArray[10].trim() : null)
}
Notice how I have to repeat the variable after the ternary operator twice. The reason I'm asking is that I've run into this same type of problem several times and it doesn't seem like the best way to handle it.
Here’s a function that wraps another function to do nothing on null and undefined values:
const liftMaybe = f => x => x == null ? null : f(x);
Then you can define a trim that does nothing to undefined:
const trimMaybe = liftMaybe(x => x.trim());
and make use of it:
var ruleObj = {
factor: trimMaybe(ruleArray[2]),
resultElseTarget: trimMaybe(ruleArray[10]),
};
It differs from your original in its handling of empty strings, but I don’t know if that was intentional or if it’s even relevant.
Conciseness is one thing but with Javascript the bigger concern is readability and type checking.
In your example, if the value of ruleArray[2] is a boolean then it'd evaluate to false and set factor to null. Maybe that's what you want but just looking at your example code right now, I'd assume your ruleArray contains bools and not potential undefines.
The better way is to write a function to do null check
EDIT: someone was faster than me :) https://stackoverflow.com/a/46436844/643084
EDIT2: the other answer is great but i'd like to just make a note. null should not be treated the same as undefined even though they evaluate the same most of the times.
Some things:
Since you're checking indexes, you'd need to make sure that you have a length of at least the size you want. Otherwise ruleArray[10] can throw you and out of range error.
Assuming you are certain that you have the right number of elements in your array, you can use this to check a var for undefined, this is common to check incoming arguments (say you had something like function ( arg1, arg2 ) ):
arg1 = arg1 || 'some_default';
In your case, again assuming your array is long enough:
factor: ( ruleArray[2] || 'some other default' );
(Why would you set it to null if that's what you are trying to avoid).
If you're wondering, "is there a way to access an index that doesn't exist and just return null", the answer is "maybe...but I wouldn't".
Note, if the value is indeed falsy (say, 0, '', or false), you may not get what you expect, in which case you'd want to check for something more explicit, like null.
I get a lot of use of out the terse "something = thisValIfNotFalsy || someOtherDefaultVal. Like anything though careful when and where, etc.
You could do something like:
var ruleArray = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
function formatRule(rule) {
if (!rule) return null
return rule.trim()
}
var ruleObj = {
factor: formatRule(ruleArray[2]),
resultElseTarget: formatRule(ruleArray[10])
}
console.log(ruleObj.factor)
console.log(ruleObj.resultElseTarget)
We created a pure function that is tasked with producing either null or a trimmed value, which avoids duplicating this logic elsewhere.
Is there a more concise way
So far all answers seem to assume your input is either a string or null / undefined. For me I'd say the check for null / undefined is the wrong way round. You can only call trim on a string, so why not check it's a string?. It would also mean NaN / Numbers / Arrays etc, would not error. I'm assuming what your wanting this function to do is trim a string if it's a string, so I would also say you should pass the original value if not a string.
Maybe that's what #zaftcoAgeiha meant when he talking about not treating null & undefined the same.
Anyway, here is an example. Notice how numbers are still passed, but hello gets trimmed.
const ruleArray = [];
ruleArray[2] = null;
ruleArray[5] = 7;
ruleArray[7] = 'Helllo ';
const trimIfString = (x) => typeof x === 'string' ? x.trim() : x;
var ruleObj = {
factor: trimIfString(ruleArray[2]),
resultElseTarget: trimIfString(ruleArray[5]),
hello: trimIfString(ruleArray[7])
}
console.log(ruleObj);
You can use a function pattern and set default parameter with AND && operator when passing the parameter to check if variable is defined, if not set element value to null. You can include further checks to determine if variable is passed is a string.
let ruleArray = [];
ruleArray[10] = "def ";
let ruleFn = (factor = null, resultElseTarget = null) =>
({factor, resultElseTarget});
let ruleObj = ruleFn(ruleArray[2] && ruleArray[2].trim()
, ruleArray[10] && ruleArray[10].trim());
console.log(ruleObj, ruleObj.factor === null);
I want to extend the number class to have instance functions such as odd and even so I can do something like this:
2.odd() => false
2.even() => true
1.even() => false
1.odd() => true
Extending classes is a good Ruby practise: "Ruby check if even number, float".
Is the same true in JavaScript, or does it cause performance issues or some other problem?
Anyway, I can't extend despite my best efforts:
var NumberInstanceExtensions = {
accuracy: function(){
return 'This is cool ' + this
}
}
$.extend(Number.prototype,NumberInstanceExtensions);
alert( $.type(5) ); //-> number
//alert( 5.accuracy() ); //-> Uncaught SyntaxError: Unexpected token ILLEGAL
http://jsfiddle.net/VLPTb/2/
How can I get this to work? The syntax error makes me think this isn't how JavaScript works on a fundamental level. Is my best bet extending the Math class and doing this instead:
Math.odd(2) => false
Math.even(2) => true
Math.even(1) => false
Math.odd(1) => true
That seems far more inelegant than 2.odd().
I think as long as you understand the side-effects of your "extension" then you're okay. I often modify the String prototype to add an "elipsis" method so I can do things like
"SomeString".elipsis()
But start at the beginning. You're not "extending classes" in JavaScript. JavaScript is a prototype-based language. You can modify prototypes to do what you need.
You won't be able to add a method directly to the number itself. You can, however modify the prototype of the Number object:
Number.prototype.even = function(){
return this.valueOf() % 2 === 0;
}
With this, you won't be able to use the following syntax:
10.even();
But, since you aren't hard-coding stuff, otherwise you wouldn't need this function anyways, you CAN do the following:
var a = 10;
a.even(); //true
I might say that you could consider adding a utilities object to do these things, because modifying primitive prototypes is not always guaranteed to be side-effect free.
This function does not really provide any gain for you. You're checking for odd and even, replacing one line of code with another. Think about the difference:
var a = 10;
var aIsEven = a.even();
vs:
var a = 10;
var aIsEven = a % 2 === 0;
You gain three characters of code, and the second option is less likely to break your "JavaScript".
You can extend natives JS objects by using (for example) Number.prototype.myFn = function(){}.
So you could do :
Math.prototype.odd = function(n){
return n % 2 === 0;
};
Math.prototype.even = function(n){
return n % 2 === 1;
};
And then use it like so :
var two = 2;
console.log(Math.odd(2)); // true
BUT I would strongly advise you against extending natives in JavaScript.
You can read more about it here
EDIT : After trying my code on JSFiddle, it appears the Math object has no prototype, you can read more about it here. The code above won't work !
Instead, you could do :
Math.odd = function(n){
return n % 2 === 0;
};
Math.even = function(n){
return n % 2 === 1;
};
console.log(Math.odd(2)); // true
or :
Number.prototype.odd = function(){
return this % 2 === 0;
};
Number.prototype.even = function(){
return this % 2 === 1;
};
console.log(new Number(2).odd()); // true
I'd like to point out that that is already available in the numbers class.
Just use the boolean methods, odd? and even?
2.odd?
=> false
2.even?
=> true
Hope this helps.
No need to create a new class, it already exists in the numbers class.
When accessing nested objects using dot notation, I always have to make sure that the previous object exists, which gets pretty exhausting.
I basically want to avoid long if chains like
if (a && a.b && a.b.c && a.b.c[0] ... ) { v = a.b.c[0]; }
The only other thing I can think of is via the use of a try catch.
var v; try { v = a.b.c[0].d.e; } catch (e) {}
Is there a better pattern for this?
I think you've got the two prettiest solutions already.
But note that for something like, say, obj.obj.string.length your first solution will fail if string === "". Since an empty string is falsey, it'll trip the && guard.
But speaking of strings, you could do something like:
function getNestedProperty(obj, propChain) {
var props = propChain.slice(0), prop = props.shift();
if(typeof obj[prop] !== "undefined") {
if(props.length) {
return getNestedProperty(obj[prop], props);
} else {
return obj[prop];
}
}
}
var v = getNestedProperty(a, ["b", "c", 0, "d", "e"]);
Yeah... not too pretty :P
I'd say that, of the solutions proposed, try...catch is probably the simplest way to go
How about this one:
var hasProperty = function (object, property) {
var properties = property.split('.'),
temp = object;
while (temp && properties.length) {
temp = temp[properties.shift()];
}
return !!temp;
};
and then use it like:
if (a && hasProperty(a, 'b.c.0' ) { v = a.b.c[0]; }
The scenario you are referring to in your question is also called "optional chaining". Some languages already support it by now – for example C# has so called null-conditional operators which allow you to short-circuit your expressions:
var count = customers?[0]?.Orders?.Count();
Unfortunately, this feature has not yet made it into the current JS specifications.
There is an open Stage 1 proposol for "optional chaining" that can be tracked here.
This would allow you to write...
a?.b[3].c?.(x).d
...instead of:
a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
If you want to take the risk and use it already at this early stage, you can target it via babel to include it in your project.
It's rather evil, but this should work and doesn't look too horrible:
var i = !a ? null : !a.b ? null : !a.b.c ? null : !a.b.c.d ? a.b.c.d.e;
The reason for the ! is to invert the test flag, to allow the success case to be the last expression in the ?:. That allows us to chain them together like this.
Do check the operator precedence if you want to do this for real (I did some very basic tests and I think I got it right). And do expect people to point and laugh if they see it in your code.
I have this piece of code:
grp = $.cookies.get('grp');
if(grp == null){grp = 0}
1- how can i write it with ternary operators?
2- in word of performance is ternary form better or simple if statement if we have lots of them?
thanks in advance
do:
grp = $.cookies.get('grp');
grp = (grp == null) ? 0 : grp;
or
grp = $.cookies.get('grp') || 0;
I think the difference in performance is negligible, the important thing in such case is readability of the code.
Added: for example, if you want to check for object and create it if does not exist, then:
some_obj = window.some_obj || {};
To avoid type coercion always use the === operator. The null check is discouraged as well. When a value is undefined javascript always returns undefined.
Most of the time it's easier to create a helper function to avoid repeated code.
var grp = defaultIfUndefined($.cookies.get('grp'), 'My default value');
function defaultIfUndefined (value, defaultValue) {
if (typeof value === 'undefined') {
return defaultValue;
}
return value;
}
We are using custom datetime picker which till now had to return both date and time. But now the conditions have changed and sometimes I need to return only date so I decided to add an optional parameter. Now the script is this :
var date_format = "dd/mm/yyyy HH:MM:ss";
function viewCalendar(parent, destField, dateOnly) {
if (typeof dateOnly !== "undefined") {
date_format = "dd/mm/yyyy";
}
//more code...
But even though it seems to work (I made only few tries) I don't like this very much, but maybe it's because I'm not used to the way of JavaScript. If it wasn't because of some sample code I would do something like :
var date_format = "dd/mm/yyyy HH:MM:ss";
var dateOnly = true;
function viewCalendar(parent, destField, dateOnly) {
if (typeof dateOnly != true) {
date_format = "dd/mm/yyyy";
}
But the few examples I saw about using optinal parameters in JS I haven't seen something like this. What is the proper way to do this kind of thing i JavaScript?
You have several choices, you have already discovered typeof. If the data is not going to be falsy unless skipped, you can use a logical OR ||, the ternary conditional operator a?b:c or an if with a logical NOT to check whether to set it or not.
You also have the choice of comparing against undefined or void 0, both of which will still work even if the parameter is expected to be falsy, with the exception of passing undefined itself as the argument.
function foo(op0, op1, op2, op3, op4, op5, op6) {
// if with logical NOT
if (!op0) op0 = 'default0';
// logical OR
op1 || (op1 = 'default1');
op2 = op2 || 'default2';
// ternary
op3 = op3 ? op3 : 'default3';
// compare, below this line with falsy args too (except explicit `undefined`)
if (op4 === undefined) op4 = 'default4';
if (op5 === void 0) op5 = 'default5';
// compare combined with logical OR
(op6 !== undefined) || (op6 = 'default6');
// log parameters to see what we have now
console.log(op0, op1, op2, op3, op4, op5, op6)
}
foo(); // default0 default1 default2 default3 default4 default5 default6
Please note that in older browsers, undefined was writeable in the global scope and that in all browsers, if you're not in the global scope, undefined can be vard or set as a parameter and so have a value which is not undefined.
If you're not okay with that, choose using the void operator over undefined.
If you understand that it can happen and don't have to worry about someone shadowing undefined, feel free to use it.
If you ever see someone do it, ask them if they could var Array for you too, or something similar, so they realise they have been silly.
I may go with a ternary operator here
date_format = dateOnly ? "dd/mm/yyyy" : date_format ;
It will look for truthyness of the dateOnly, and either override the format or will retain it
Also
if (typeof dateOnly != true) {
date_format = "dd/mm/yyyy";
}
seems wrong, it could be
if (dateOnly == true) {
date_format = "dd/mm/yyyy";
}
Another way is to use an anonymous object as a single parameter:
function viewCalendar(opts) {
if (opts.dateOnly) {
date_format = "dd/mm/yyyy";
}
}
viewCalendar({parent:foo, destField:bar, dateOnly:true})
Especially useful when you have a lot of optional parameters.