Replace conditional operator with if/else automatically? - javascript

A specific JS parser (not in my control) does not understand nested conditional operator syntax like this:
return num === 1 ? condition ? condition : something : something;
Hence, I would like to replace all conditional operators (simple and nested) in a file with if/else blocks. How do I go about it? (A regexp for Textmate or similar would be helpful.)

How do I go about it? (A regexp for Textmate or similar would be helpful.
I don't think this is possible with regular expressions - you would need to parse the whole JS expression grammar with them. Of course you might use them to find appearances of ternary operators, but replacing them would need to be done by hand.
This is because conditional operators form expressions, while if/else structures are statements. That means you will need to lift the statement containing the ternary operator around the if-else. There are two solutions to transform the general
<some statement ( condition ? then-expression : else-expression) >
if ( condition ) {
<some statement then-expression>
} else {
<some statement else-expression>
}
var helper;
if ( condition )
helper = then-expression;
else
helper = else-expression;
<some statement helper>
Which of them to choose depends on the complexity of the some statement (for your return-statement I'd opt for #1). And since the replacements bring their own syntax rules, you even might need to adapt the surrounding blocks. All this is not a trivial task, and imho could be automated only if you already had a parsed AST to transform.

One idea would be to transform x ? y : z into (x && y) || z. You're still probably going to have to do some fairly fancy parsing to find the ternary operators, but at least you can leave them as expressions rather than statements which will mean the changes needed are much less extensive. Note however that there might be cases where these two things are not exactly equivalent (e.g. if y is 0) so use this approach with caution and only when you have some control over what code it will be applied to.
The idea of (x && y) || z is that when x is truthy, it evaluates to y, and when x is falsey, it evaluates to z. This works due to some edge-cases in the way that JavaScript handles short-circuiting for non-Boolean values. It's sort of a generalization of standard boolean arithmetic. Essentially, JavaScript will return the last value that it needed to inspect in order to perform the operation. So true && (stuff) returns stuff, whereas false && (stuff) returns false. Likewise true || (stuff) returns true, whereas false || (stuff) returns stuff.
There are two cases to check to show that (x && y) || z (mostly) does what we want:
Case 1: x is truthy
Since x is truthy, (true && y) || z evaluates to y || z. If y is also truthy, then this evaluates to y and we get our desired behavior. If y is not truthy, the operation fails and we get z instead!
Case 2: x is falsey
Since x is falsey, (false && y) || z evaluates to false || z, which in turn evaluates to z. Here we are lucky and always get the desired behavior regardless of y.
Example
Here's an example of the desired behavior in the chrome console:
> var x1 = "a" //truthy
> var x2 = "" //falsey
> var y = "y"
> var z = "z"
> (x1 && y) || z
"y"
> (x2 && y) || z
"z"
Just watch out for cases where y is not truthy!
> var y2 = 0
> (x1 && y2) || z
"z"

For future researchers, I'd like to offer my solution approach for these cases where I want to improve conditional statements, either through the ternary operator, switch statements, or traditional if/if else/else statements.
It is already known by many that the use of an object literal for this purpose is a good practice, especially when you have several options and a given parameter that you can use to index this object. Give an example below.
ternary operator version
const getAnimalSpecies = (sound) =>
sound === 'bark'
? 'Dogs'
: sound === 'meow'
? 'Cats'
: sound === 'howl'
? 'Wolfs'
: 'Not animal found';
switch statement version
const getAnimalSpecies = (sound) => {
switch (sound) {
case 'bark':
return 'Dogs'
case 'meow':
return 'Cats'
case 'howl':
return 'Wolfs'
default:
return 'Not animal found'
}
}
if/if else/else statement version
const getAnimalSpecies = (sound) => {
if (sound === 'bark') {
return 'Dogs';
} else if (sound === 'meow') {
return 'Cats';
} else if (sound === 'howl') {
return 'Wolfs';
} else {
return 'Not animal found';
}
};
object literal version
const getAnimalSpecies = (sound) => {
const animalSounds = {
bark: 'Dogs',
meow: 'Cats',
howl: 'Wolfs'
}
return animalSounds[sound] ?? 'Not animal found'
}
All these statements return the same results!
getAnimalSpecies('bark') // Dogs
getAnimalSpecies('purring') // Not animal found
But I think you can agree with me that the last one it's prettier and cleaner.
Beyond that, I found myself in a different situation, where the results were given by Booleans obtained by the parameter already processed. Sound complex ha 🤭!? Lets put two examples.
First one
ternary operator version
const imageExtensionAtEnd = /\.(jpe?g|png|gif|tiff?|webp|bpm|jfif)$/i;
const pdfExtensionAtEnd = /\.(pdf)$/i;
const audioExtensionAtEnd = /\.(mp3|amr|aac)$/i;
const videoExtensionAtEnd = /\.(ogm|wmv|webm|ogv|mov|asx|m4v|mp4|avi|3gp|mkv|mpe?g)$/i;
const getFileType = (file) => {
const type = imageExtensionAtEnd.test(file)
? 'image'
: videoExtensionAtEnd.test(file)
? 'video'
: pdfExtensionAtEnd.test(file)
? 'application'
: audioExtensionAtEnd.test(file)
? 'audio'
: undefined;
return type;
};
object literal version
export const getFileType = (file) => {
const fileTypes = {
[imageExtensionAtEnd.test(file)]: 'image',
[videoExtensionAtEnd.test(file)]: 'video',
[pdfExtensionAtEnd.test(file)]: 'application',
[audioExtensionAtEnd.test(file)]: 'audio'
};
return fileTypes['true'] ?? undefined;
};
Second one
conditional version
import { useMediaQuery } from '#material-ui/core';
const defaulValues = ['', '', '', '', ''];
const useMediaVariant = (values = defaulValues) => {
const [xs, sm, md, lg, xl] = values;
if (useMediaQuery('(max-width:600px)')) {
return xs;
}
if (useMediaQuery('(min-width:600px)')) {
return sm;
}
if (useMediaQuery('(min-width:960px)')) {
return md;
}
if (useMediaQuery('(min-width:1280px)')) {
return lg;
}
if (useMediaQuery('(min-width:1920px)')) {
return xl;
} else {
return values.at(-1);
}
};
object literal version
const useMediaVariant = (values = defaulValues) => {
const [xs, sm, md, lg, xl] = values;
const variant = {
[useMediaQuery('(max-width:600px)')]: xs,
[useMediaQuery('(min-width:600px)')]: sm,
[useMediaQuery('(min-width:960px)')]: md,
[useMediaQuery('(min-width:1280px)')]: lg,
[useMediaQuery('(min-width:1920px)')]: xl
};
return variant['true'] || values.at(-1);
};
export default useMediaVariant;
It is used liked this example:
import useMediaVariant from '.../hooks/useMediaVariant';
const valueHeading = useMediaVariant(['subtitle2', 'h8'])
<Typography variant={valueHeading} />
The second one is a custom hook I create to apply responsiveness in some material-ui components props...
Note that when you use the object['true'] method the last occurrence found will be the one returned. Important thing if there is more than one truthy possibility.
Hope to help someone.

Related

In Angular, what does the double pipe ('||') mean? [duplicate]

People often write this in order to specify default values:
var thing = this || that;
which is, AFAIK, the same thing as this:
var thing = !!this ? this : that;
What do you call the technique used to specify defaults in the first code block?
NOTE: I am not asking what a logical OR is called. I am asking what the alternative to ternary notation (as written in the first code block) is called.
I'd call:
var a = A || B;
conditional assignment, since it is effectively:
if (!!A) {
a = A;
} else {
a = B;
}
and it is a replacement for the conditional operator : ?
var a = A? A : B;
It might also be called "logical assignment" since it involves a logical OR expression, but it doesn't seem to fit with what it's doing.
As mentioned elsewhere it is a logical OR.
The evaluation in question is a short-circuit evaluation.
It might help to look at it like this:
if ((foo = bar)) {
} else {
foo = baz;
}
The if statement evaluates to the value of bar. If bar is false, null etc the evaluation would be false.
Edit: Note:
It is perfectly valid to evaluate an assignment. If we say:
if ((a = b)) { ...
note that it is not:
if (a === b) { ...
the evaluation is done on the result of the assignment. Here it would evaluate to true if (b).
One should however always wrap them in parenthesis. This makes it clear that we are evaluating the assignment and not comparing the variables.
If one do not like it that is fair enough, (I'm rather used to it from C), but in this case it is merely for the sake of the answer to the question.
In the same way we have:
if ((foo = foo)) {
} else {
foo = baz;
}
var x = false;
console.log((x = x)); // False
As such we can say:
(x = x) || (x = y)
And to make it short:
x = (x || y);
or shorter:
x = x || y;
The double pipe is called the 'or' operator.
The double pipe is the Logical OR operator in JavaScript.
If the technique had a name I guess it would be "(ab)using the short-circuiting of the logical OR operator"

Is there a way to generate a conditional expression which can be directly executed by if?

For example, can I create a method which can return me an expression that can be evaluated by if?
function getCondition(variable, value, operator)//not sure what params to pass
{
var condition = false; //initialized to false
//generate condition based on parameter passed
return condition;
}
and then use it directly
if ( getCondition( a, 5, "<" ) ){ console.log("correct") }
Yes.
In your example, which probably is not your actual use-case, you'd simply have to map your operator:
function getCondition( x, y, op ) {
switch ( op ) {
case '<':
return x < y
case '>':
return x > y
default:
throw new Error( 'operator not understood' )
}
}
if ( getCondition( 1, 5, '<' ) ) {
...
}
You might see this pattern commonly in something like a physics simulation, where you need operators that do not exist natively, such as dot or cross products. I've never seen a use-case where you'd want to pass that operator explicitly to a function though, rather, just create the functions you need for each operator.
You could pass the expression as a parameter
var a = 3.5;
function getCondition(bool) {
var condition = false;
return bool || condition
}
if (getCondition(a < 5)) {
console.log("correct")
}
You probably want to evaluate arguments when you apply the condition, not when you define it. Here's one possibility:
var operator = {};
operator.greaterThan = function(val) {
return function(x) {
return x > val;
}
};
operator.lessThan = function(val) {
return function(x) {
return x < val;
}
};
isLessThan5 = operator.lessThan(5);
a = 4;
if(isLessThan5(a)) console.log('ok'); else console.log('not ok');
b = 10;
if(isLessThan5(b)) console.log('ok'); else console.log('not ok');
For complex conditions you can also add boolean operators:
operator.and = function() {
var fns = [].slice.call(arguments);
return function(x) {
return fns.every(f => f(x));
}
};
operator.or = function() {
var fns = [].slice.call(arguments);
return function(x) {
return fns.some(f => f(x));
}
};
isBetween5and10 = operator.and(
operator.greaterThan(5),
operator.lessThan(10));
if(isBetween5and10(8)) console.log('ok')
if(isBetween5and10(15)) console.log('ok')
Yes, but you have to define in the function what the operator means. So your function needs to contain some code along the lines of:
if (operator === '>') {
condition = (value1 > value2);
}
You could also use string concatenation and eval, but I wouldn't recommend it:
condition = eval(value1 + operator + value2);
Yes, you can use the return value of a method if it can be evaluated to either true or false.
The sample code you provided should work as you expect it.
The return value of the method can also be evaluated from an int or a string to a boolean value. Read more about that here: JS Type Coercion
It is possible to pass a function or expression to an if. Like you're saying yourself, an if accepts an expression... that evaluates to either true or false. So you can create any function or method that returns a boolean value (not entirely true in PHP and other weak typed languages).
Clearly, since PHP isn't strongly typed, no function guarantees that it returns a boolean value, so you need to implement this properly yourself, as doing this makes you prone to getting errors.

What do you call the double pipe in javascript, as used in this context?

People often write this in order to specify default values:
var thing = this || that;
which is, AFAIK, the same thing as this:
var thing = !!this ? this : that;
What do you call the technique used to specify defaults in the first code block?
NOTE: I am not asking what a logical OR is called. I am asking what the alternative to ternary notation (as written in the first code block) is called.
I'd call:
var a = A || B;
conditional assignment, since it is effectively:
if (!!A) {
a = A;
} else {
a = B;
}
and it is a replacement for the conditional operator : ?
var a = A? A : B;
It might also be called "logical assignment" since it involves a logical OR expression, but it doesn't seem to fit with what it's doing.
As mentioned elsewhere it is a logical OR.
The evaluation in question is a short-circuit evaluation.
It might help to look at it like this:
if ((foo = bar)) {
} else {
foo = baz;
}
The if statement evaluates to the value of bar. If bar is false, null etc the evaluation would be false.
Edit: Note:
It is perfectly valid to evaluate an assignment. If we say:
if ((a = b)) { ...
note that it is not:
if (a === b) { ...
the evaluation is done on the result of the assignment. Here it would evaluate to true if (b).
One should however always wrap them in parenthesis. This makes it clear that we are evaluating the assignment and not comparing the variables.
If one do not like it that is fair enough, (I'm rather used to it from C), but in this case it is merely for the sake of the answer to the question.
In the same way we have:
if ((foo = foo)) {
} else {
foo = baz;
}
var x = false;
console.log((x = x)); // False
As such we can say:
(x = x) || (x = y)
And to make it short:
x = (x || y);
or shorter:
x = x || y;
The double pipe is called the 'or' operator.
The double pipe is the Logical OR operator in JavaScript.
If the technique had a name I guess it would be "(ab)using the short-circuiting of the logical OR operator"

Why does ( true && 1 ) return 1, but ( 1 && true ) returns true?

In C I know true and false evaluate to 1 and 0 respectively. show in this case just prints to the screen... Not sure what's going on here. I'm expecting to get true back. This 1 is messing up my karma.
show(1 && true);
true
show(true && 1);
1
Simply put - that's how && is defined. In Javascript, a && b returns a if a is falsy and b if a is truthy.
Conversely a || b returns a if a is truthy and b if a is falsy.
This makes sense intuitively - if a is false in a && b, then why bother reading the rest of the expression? You already know the whole thing is false. So just return false. But Javascript makes the decision to return a, which is falsy, instead of making up the value false to return out of nowhere.
This is based on short-circuit evaluation common to all C-style languages.
It allows for much expressiveness in Javascript. For instance this pattern:
var foo = function(opts) {
opts = opts || {}
// ...
}
Implements an optional parameter opts. If opts is not passed in at all, opts = opts || {} will set opts to {}, so the rest of the code does not have to know opts wasn't passed.
In long-hand it is equivalent to the following:
var x = a || b; // is equivalent to
var x;
if(a) {
x = a;
}
else {
x = b;
}
and
var y = a && b; // is equivalent to
var y;
if(!a) {
y = a;
}
else {
y = b;
}
Therefore Javascript can be much more terse than C or Java, because simple if statements can be replaced by || or && entirely. Sometimes this makes the code more terse and less readable and more like Perl, other times it allows for new Javascript patterns, like opts = opts || {}.
Another use is in patterns like
var displayName = user.fullname || user.email;
Which means "use the full name if available; if not, fall back to email." I personally find this expressive and readable, but it's arguably terse and obscure depending on which part of the Javascript community you hail from. Because of examples like this, and essentially the fact that truthy values are far more diverse then falsy values, using short-circuit || is much more common than short-circuit &&, as in your question.

Testing nested objects as undefined in Javascript [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
javascript test for existence of nested object key
I'm attempting to construct an error message for a formset by testing if a certain object is not undefined, and if it's not undefined, then I end up populating it with that error message. The main problem is that I have to validate if each nested object is undefined, which results in some pretty ugly code. Here's the example:
errorsForField: function(fieldName, formsetName, formNumber) {
if (typeof this.model.errors != 'undefined'){
var fieldError = document.createElement('span');
$(fieldError).addClass('field-error');
// THE FOLLOWING LINE THROWS ERROR.
if (formsetName && _.isUndefined(this.model.errors[formsetName][fieldName]) != true) {
$(fieldError).text(this.model.errors[formsetname][fieldName]);
} else if (typeof this.model.errors[fieldName] != "undefined"){
$(fieldError).text(this.model.errors[fieldName]);
}
this.errors[fieldName] = fieldError.outerHTML;
return fieldError.outerHTML;
}
return false;
},
I get an error stating that I cannot determine [fieldName] of an undefined object this.model.errors[formsetName]. In other words, I have to first determine if this.model.errors[formsetName] is empty and then test if [fieldname] is undefined.
This seems like a really cumbersome solution. Any suggestions for changing this?
You can create a library function that takes property names as parameters and returns the final value if it exists, or null:
function TryGetPropertyValue(o, propertyName1 /*, ... propertyNameN */) {
var names = [].slice.call(arguments, 1);
while (o && names.length) {
o = o[names.shift()];
}
return names.length ? null : o;
}
Call it like:
var err = TryGetPropertyValue(this.model.errors, formsetName, fieldName) ||
TryGetPropertyValue(this.model.errors, fieldName);
if (err != null) {
$(fieldError).text(err);
}
If you want it to return undefined instead of null if the field is not found, you can change the function slightly:
function TryGetPropertyValue(o, propertyName1 /*, ... propertyNameN */) {
var names = [].slice.call(arguments, 1);
while (o && names.length) {
o = o[names.shift()];
}
if (names.length == 0) {
return o;
}
}
http://jsfiddle.net/HbggQ/
As Paul suggested, this is an inherent limitation of Javascript. Even Coffeescript (which is just a layer of syntactic sugar on top of JS) doesn't really solve the problem; it just hides the workaround under it's syntactic sugar (which admittedly is really handy)
If you want to stick to Javascript, you basically have two options: use ternary operators, or use boolean operators. Here's examples of each that check A.B.C.D (where A, B, C or D might not exist):
// Returns A.B.C.D, if it exists; otherwise returns false (via ternary)
return !A ? false :
!A.B ? false :
!A.B.C ? false :
A.B.C.D ? A.B.C.D : false;
// Returns A.B.C.D, if it exists; otherwise returns false (via booleans)
return A && A.B && A.B.C && A.B.C.D;
Obviously the latter is a lot shorter. Both solutions rely on Javascript's "truthiness" (ie. that the values 0, "", null, and undefined count as false). This should be fine for your case, as none of those values will have an errors property. However, if you did need to distinguish between (say) 0 and undefined, you could use the ternary style, and replace !A with typeof(A) == 'undefined'.

Categories