Alternative to nested ternary operator in JS
I personally love ternary operators, and in my humble opinion, they make complicated expressions very easy to digest. Take this one:
const word = (distance === 0) ? 'a'
: (distance === 1 && diff > 3) ? 'b'
: (distance === 2 && diff > 5 && key.length > 5) ? 'c'
: 'd';
However in our project's ESLINT rules nested ternary operators are forbidden, so I have to get rid of the above.
I'm trying to find out alternatives to this approach. I really don't want to turn it into a huge if / else statement, but don't know if there's any other options.
Your alternatives here are basically:
- That
if
/else
you don't want to do - A
switch
combined withif
/else
I tried to come up with a reasonable lookup map option, but it got unreasonable fairly quickly.
I'd go for #1, it's not that big:
if (res.distance == 0) {
word = 'a';
} else if (res.distance == 1 && res.difference > 3) {
word = 'b';
} else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) {
word = 'c';
} else {
word = 'd';
}
If all the braces and vertical size bother you, without them it's almost as concise as the conditional operator version:
if (res.distance == 0) word = 'a';
else if (res.distance == 1 && res.difference > 3) word = 'b';
else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) word = 'c';
else word = 'd';
(I'm not advocating that, I never advocate leaving off braces or putting the statement following an if
on the same line, but others have different style perspectives.)
#2 is, to my mind, more clunky but that's probably more a style comment than anything else:
word = 'd';
switch (res.distance) {
case 0:
word = 'a';
break;
case 1:
if (res.difference > 3) {
word = 'b';
}
break;
case 2:
if (res.difference > 5 && String(res.key).length > 5) {
word = 'c';
}
break;
}
And finally, and I am not advocating this, you can take advantage of the fact that JavaScript's switch
is unusual in the B-syntax language family: The case
statements can be expressions, and are matched against the switch value in source code order:
switch (true) {
case res.distance == 0:
word = 'a';
break;
case res.distance == 1 && res.difference > 3:
word = 'b';
break;
case res.distance == 2 && res.difference > 5 && String(res.key).length > 5:
word = 'c';
break;
default:
word = 'd';
break;
}
How ugly is that? :-)
To my taste, a carefully structured nested ternary beats all those messy ifs and switches:
const isFoo = res.distance === 0;
const isBar = res.distance === 1 && res.difference > 3;
const isBaz = res.distance === 2 && res.difference > 5 && String(res.key).length > 5;
const word =
isFoo ? 'a' :
isBar ? 'b' :
isBaz ? 'c' :
'd' ;
You could write an immediately invoked function expression to make it a little more readable:
const word = (() => {
if (res.distance === 0) return 'a';
if (res.distance === 1 && res.difference > 3) return 'b';
if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) return 'c';
return 'd';
})();
Link to repl
We can simplify it using basic operators like && and ||
let obj = {}
function checkWord (res) {
return (res.distance === 0) && 'a'
|| (res.distance === 1 && res.difference > 3) && 'b'
|| (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) && 'c'
|| 'd';
}
// case 1 pass
obj.distance = 0
console.log(checkWord(obj))
// case 2 pass
obj.distance = 1
obj.difference = 4
console.log(checkWord(obj))
// case 3 pass
obj.distance = 2
obj.difference = 6
obj.key = [1,2,3,4,5,6]
console.log(checkWord(obj))
// case 4 fail all cases
obj.distance = -1
console.log(checkWord(obj))