How to write a switch statement that compares multiple variables? - javascript

I'm a beginner in JS, I was hoping this code worked but it doesn’t, what would be an alternative way of accomplishing the result I'm expecting here? basically want to return something based on the combination of 3 variables/arguments. (btw, in some case statements I'm not mentioning the 'roomSize' value because I'm expecting the code to know that it's the same as the original value)
const roombaMovement = (direction , initialRoombaLocation, roomSize) => {
const roombaLocation = initialRoombaLocation
let wallHit = 0;
switch (roombaLocation && direction && roomSize) {
case [4,0] && "S" && [4,0]:
wallHit +=1;
break;
case [0,0] && "W":
wallHit +=1;
break;
case [1,0] && "S":
wallHit +=1;
break;
case [0,1] && "W":
wallHit +=1;
break;
default:
}
return wallHit;
}

Don't use switch for this - it's quite verbose and will be tedious to get the syntax to work properly for the logic you want. Since all the resulting statements to execute are the same (increment wallHit), all you need is a single (but large) if statement:
const arrsEqual = (a, b) => a.length === b.length && a.every((num, i) => num === b[i]);
const roombaMovement = (direction , initialRoombaLocation, roomSize) => {
const roombaLocation = initialRoombaLocation
let wallHit = 0;
if (
(direction === 'S' && arrsEqual(roombaLocation, [4, 0]) && arrsEqual(roomSize, [4, 0])) ||
(direction === 'W' && arrsEqual(roombaLocation, [0, 0])) ||
(direction === 'S' && arrsEqual(roombaLocation, [1, 0])) ||
(direction === 'W' && arrsEqual(roombaLocation, [0, 1]))
) {
wallHit +=1;
}
return wallHit;
}

you can also use JSON.stringify()
and compose a character string gathering all of your values:
const roombaMovement = (direction , initialRoombaLocation, roomSize) =>
{
let wallHit = 0
, testVal = `${JSON.stringify(initialRoombaLocation)} && "${direction}"`
;
if (testVal==='[4,0] && "S"')
{
testVal += ` && ${JSON.stringify(roomSize)}`;
}
switch (testVal)
{
case '[4,0] && "S" && [4,0]':
case '[1,0] && "S"':
case '[0,0] && "W"':
case '[0,1] && "W"':
++wallHit;
break;
}
return wallHit;
}
which is gives a little more readable code ;)

Taking a guess at the actual intent of the code…
Define what a wall is. For example:
const isWall = ([x, y], [roomWidth, roomHeight]) =>
x === -1
|| y === -1
|| x === roomWidth + 1 // check which of roomWidth or roomWidth + 1 is correct
|| y === roomHeight + 1; // same here
Check whether the movement brings you into one:
const roombaMovement = (direction, initialRoombaLocation, roomSize) => {
const [x, y] = initialRoombaLocation;
const [dx, dy] = direction;
const newLocation = [x + dx, y + dy];
return isWall(newLocation, roomSize);
};

here are some pointers for you:
don't change the order of your parameters. (direction , initialRoombaLocation, roomSize) vs (roombaLocation && direction && roomSize). It's just better coding to be consistent.
There's no reason to assign a variable into another variable: const roombaLocation = initialRoombaLocation, just use initialRoombaLocation.
This is not valid syntax: && "S". maybe you meant: && direction == "S"
roombaLocation == [1, 0] is not valid syntax. you will need to find a better was to check that condition.
There's no reason to use a case statement if there's only one possible result. just use a simple if statement.
good luck!

Related

I failed Javascript tech interview but I dont know why

I was only allowed to use google document for writing.
Could you please tell me what I did wrong? The recruiter wont get back to me when I asked her why I failed
Task 1:
Implement function verify(text) which verifies whether parentheses within text are
correctly nested. You need to consider three kinds: (), [], <> and only these kinds.
My Answer:
const verify = (text) => {
   const parenthesesStack = []; 
   
  for( let i = 0; i<text.length; i++ ) {
const closingParentheses = parenthesesStack[parenthesesStack.length - 1]
if(text[i] === “(”  || text[i] === “[” || text[i] === “<”  ) {
parenthesisStack.push(text[i]);
} else if ((closingParentheses === “(” && text[i] === “)”) || (closingParentheses === “[” && text[i] === “]”) || (closingParentheses === “<” && text[i] === “>”) ) {
   parenthesisStack.pop();
} 
  };
return parenthesesStack.length ? 0 : 1;  
}
Task 2:
Simplify the implementation below as much as you can.
Even better if you can also improve performance as part of the simplification!
FYI: This code is over 35 lines and over 300 tokens, but it can be written in
5 lines and in less than 60 tokens.
Function on the next page.
// ‘a’ and ‘b’ are single character strings
function func2(s, a, b) {
var match_empty=/^$/ ;
if (s.match(match_empty)) {
return -1;
}
var i=s.length-1;
var aIndex=-1;
var bIndex=-1;
while ((aIndex==-1) && (bIndex==-1) && (i>=0)) {
if (s.substring(i, i+1) == a)
aIndex=i;
if (s.substring(i, i+1) == b)
bIndex=i;
i--;
}
if (aIndex != -1) {
if (bIndex == -1)
return aIndex;
return Math.max(aIndex, bIndex);
} else {
if (bIndex != -1)
return bIndex;
return -1;
}
};
My Answer:
const funcSimplified = (s,a,b) => {
if(s.match(/^$/)) {
return -1;
} else {
return Math.max(s.indexOf(a),s.indexOf(b))
}
}
For starters, I'd be clear about exactly what the recruiter asked. Bold and bullet point it and be explicit.
Secondly, I would have failed you from your first 'for' statement.
See my notes:
// Bonus - add jsdoc description, example, expected variables for added intention.
const verify = (text) => {
// verify what? be specific.
const parenthesesStack = [];
for( let i = 0; i<text.length; i++ ) {
// this could have been a map method or reduce method depending on what you were getting out of it. Rarely is a for loop like this used now unless you need to break out of it for performance reasons.
const closingParentheses = parenthesesStack[parenthesesStack.length - 1]
// parenthesesStack.length - 1 === -1.
// parenthesesStack[-1] = undefined
if(text[i] === “(” || text[i] === “[” || text[i] === “<” ) {
parenthesisStack.push(text[i]);
// “ will break. Use "
// would have been more performant and maintainable to create a variable like this:
// const textOutput = text[i]
// if (textOutput === "(" || textOutput === "[" || textOutput === "<") {
parenthesisStack.push(textOutput)
} else if ((closingParentheses === “(” && text[i] === “)”) || (closingParentheses === “[” && text[i] === “]”) || (closingParentheses === “<” && text[i] === “>”) ) {
parenthesisStack.pop();
// There is nothing in parenthesisStack to pop
}
};
return parenthesesStack.length ? 0 : 1;
// Will always be 0.
}
Not exactly what the intention of your function or logic is doing, but It would fail based on what I can see.
Test it in a browser or use typescript playground. You can write javascript in there too.
Hard to tell without the recruiter feedback. But i can tell that you missundertood the second function.
func2("mystrs", 's', 'm') // returns 5
funcSimplified("mystrs", 's', 'm') // returns 3
You are returning Math.max(s.indexOf(a),s.indexOf(b)) instead of Math.max(s.lastIndexOf(a), s.lastIndexOf(b))
The original code start at i=len(str) - 1 and decrease up to 0. They are reading the string backward.
A possible implementation could have been
const lastOccurenceOf = (s,a,b) => {
// Check for falsyness (undefined, null, or empty string)
if (!s) return -1;
// ensure -1 value if search term is empty
const lastIndexOfA = a ? s.lastIndexOf(a) : -1
const lastIndexOfB = b ? s.lastIndexOf(b) : -1
return Math.max(lastIndexOfA, lastIndexOfB)
}
or a more concise example, which is arguably worse (because less readable)
const lastOccurenceOf = (s,a,b) => {
const safeStr = s || '';
return Math.max(safeStr.lastIndexOf(a || undefined), safeStr.lastIndexOf(b || undefined))
}
I'm using a || undefined to force a to be undefined if it is an empty string, because:
"canal".lastIndexOf("") = 5
"canal".lastIndexOf(undefined) = -1
original function would have returned -1 if case of an empty a or b
Also, have you ask if you were allowed to use ES6+ syntax ? You've been given a vanilla JS and you implemented the equivalent using ES6+. Some recruiters have vicious POV.

can't pass challenge if code not optimized further

I can't pass this coding challenge: Code Challenge: https://www.codewars.com/kata/550f22f4d758534c1100025a/train/javascript
because my code is TOO SLOW. I'm not sure which part of my code is causing the problem. That's why I need help to optimize it.
function dirReduc(arr){
if (arr.length === 0 || arr.length === 1) return [];
let lengthTracker = arr.length;
for (let i = 0; i < arr.length; i++) {
if (lengthTracker > arr.length) {
lengthTracker = arr.length;
i = 0;
}
switch(arr[i]) {
case "NORTH":
arr[i-1] === "SOUTH"? arr.splice(i-1,2) :
arr[i+1] === "SOUTH"? arr.splice(i,2) : null
break;
case "SOUTH":
arr[i-1] === "NORTH"? arr.splice(i-1,2) :
arr[i+1] === "NORTH"? arr.splice(i,2) : null
break;
case "EAST":
arr[i-1] === "WEST"? arr.splice(i-1,2) :
arr[i+1] === "WEST"? arr.splice(i,2) : null
break;
case "WEST":
arr[i-1] === "EAST"? arr.splice(i-1,2) :
arr[i+1] === "EAST"? arr.splice(i,2) : null
break;
}
i===arr.length-1? i=0:null
}
return arr;
}
Splicing can be expensive. We can form a recurrence that assumes the function has already correctly reduced the next part of the list:
function matches(a, b){
return (a == "NORTH" && b == "SOUTH") ||
(b == "NORTH" && a == "SOUTH") ||
(a == "EAST" && b == "WEST") ||
(b == "EAST" && a == "WEST");
}
function f(A, i=0){
if (i == A.length)
return [];
const rest = f(A, i + 1);
const [head,...tail] = rest;
if (head){
if (matches(A[i], head))
return tail;
else
return [A[i]].concat(rest);
}
return [A[i]];
}
I see several problems with this. First, as I mentioned in the comments, splicing long arrays is costly and makes your algorithm O(n^2). Simple and faster would be to use a read-point and a write-point to copy the elements into itself one cell at a time, just skipping over the annihilations and then use splice once at the end to trim the uncopied cells off the end of the array. This would make it O(n).
Secondly, your code is looking both forward and backward for matches which is both unnecessary and can be confusing. Finally, there's no need for a switch (...) as all of the branches do the same thing.
Here is how I would use your code to accomplish this, changing the things mentioned above and noted in the comments.
function dirReduc(arr){
if (arr.length === 0 || arr.length === 1) return [];
let lengthTracker = 0; // the write-point
for(let i = 0; i < arr.length; i++) { // i is the read-point
if(lengthTracker == 0) {
// if no output, copy readpoint to write-point and advance
arr[lengthTracker++] = arr[i];
} else {
// replaces switch()
if (((arr[lengthTracker-1] === "NORTH") && (arr[i] === "SOUTH"))
|| ((arr[lengthTracker-1] === "SOUTH") && (arr[i] === "NORTH"))
|| ((arr[lengthTracker-1] === "EAST") && (arr[i] === "WEST"))
|| ((arr[lengthTracker-1] === "WEST") && (arr[i] === "EAST"))) {
lengthTracker--; // annihilate by decrementing the writepoint
} else {
// copy readpoint to writepoint and advance
arr[lengthTracker++] = arr[i];
}
}
}
//trim the array to only include what was written
arr.splice(lengthTracker);
return arr;
}

Only evaluate parts of a condition when the corresponding flag is set

For example, I have flag1, flag2, flag3,..., flagN as boolean values for flags.
I need to write an if statement like this: If flagK is false, then turn off the "K" part of the condition:
if (condition0 && (flag1 && condition1) && (flag2 && condition2) ... && (flagN && conditionN))
{
// do something
}
// For example, if flag 2 is false then the condition should only be:
if (condition0 && (flag1 && condition1) && ... && (flagN && conditionN))
{
//do something}
}
Particularly, given an example like this (for demo only not my real problem):
const divby2 = false; //if this is false, then ignore the **i%2 === 0** below
const divby3 = true;
const divby4 = true;
const divby5 = true;
//......const divbyN....
const array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,42,45,241,526]
array.forEach((i) => {
if(i >= 0 && (divby2 && i%2 === 0) && (divby3 && i%3 === 0)) {
console.log(i) // output here should be 3,6,9,12 instead of nothing
}
}
)
Example Result:
The term you are looking for is "short-circuit" similar to the way in real electronic circuit if some part is not working you just short circuit it and by pass flow to rest
if(i >= 0 && ( divby2 && i%2 === 0 || !divby2) && (divby3 && i%3 === 0))
In this case if you are wanting that filtered number should be divisible by 2 that time you set divby2 = true
And when you just want to ignore and don't care about the divisibility by 2 you set divby2 = false
In pseudo
(is-checking-for-divby-2? AND is-current-number-divby-2?) OR (NOT is-checking-for-divby-2?)
As soon as you are not checking for divby 2 you make this logic fragment true so it won't affect evaulation of the follolwing logic fragments
And..Why should you bother making this fragments TRUE?
Because you ANDing them
Similarly you can go for divby3, divby4 ...
I would have an object with your conditions, and then filter out the functions you don't want to run, and then just reduce the object to a single function which runs all of the enabled functions:
const conditions = {
divby2: i => i % 2 === 0,
divby3: i => i % 3 === 0,
};
const enabled = {
divby2: false, //if this is false, then need to ignore the **i%2 === 0** below
divby3: true
}
const enabledConditions = (i) => {
return Object.entries(conditions).filter(
([key, value]) => enabled[key]
).reduce((carry, [_, condition]) => {
return condition(i) && carry
}, i !== false);
}
//......const divbyN....
const array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,42,45,241,526]
array.forEach((i) => {
if(i >= 0 && enabledConditions(i)){
console.log(i) //output here should be 3,6,9,12 instead of nothing
}
}
)

handle empty strings at array.sort(?) callback in chrome is very slow

in js i have to sort a lot of array elements(100k-1kk).
İn production its possible to have many blank ('') strings.
in my sort function i handle empty values - so that this values always come last .its ok.. until i have many null or undefined or blank('') values in data
if data have many nulls for example or blank strings performance is veeery slow.
And the main thing is that this fragment very slow at Chrome (at least last version for now 49.0.2623.110 m)
firefox(45.0.1) works very well (even with standart case without empty data my test x10 faster ??)
just test.with chrome and firefox
P.S. i know jsperf is more preferable for that.anyway
https://jsfiddle.net/3h0gtLu2/18/
data = []
var i = 0;
while (i++ <1000){
data.push('' + i)
}
while (i++ < 20000){
data.push(''+i )
}
var start = window.performance.now()
data.sort(function(a,b){
if (a == null || a == undefined)
return 1;
else if ( b == null || b == undefined)
return -1;
return (parseInt(a) - parseInt(b));
})
$('#time0').html($('#time0').html() + (window.performance.now() - start))
data = []
var i = 0;
while (i++ <1000){
data.push('' + i)
}
while (i++ < 20000){
data.push(null )
}
var start = window.performance.now()
data.sort(function(a,b){
if (a == '' || a === null || a == undefined)
return 1;
else if ( a == '' || b === null || b == undefined)
return -1;
return (parseInt(a) - parseInt(b));
})
$('#time1').html($('#time1').html() + (window.performance.now() - start))
data = []
var i = 0;
while (i++ <1000){
data.push('' + i)
}
while (i++ < 20000){
data.push('' )
}
var start = window.performance.now()
data.sort(function(a,b){
if ( a == null || a == undefined)
return 1;
else if ( b == null || b == undefined)
return -1;
return (parseInt(a) - parseInt(b));
})
$('#time2').html($('#time2').html() +( window.performance.now() - start))
data = []
var i = 0;
while (i++ <1000){
data.push('' + i)
}
while (i++ < 20000){
data.push('' )
}
var start = window.performance.now()
data.sort(function(a,b){
if (a == '' || a == null || a == undefined)
return 1;
else if (b == '' || b == null || b == undefined)
return -1;
return (parseInt(a) - parseInt(b));
})
$('#time3').html($('#time3').html() +( window.performance.now() - start))
In order to ensure that your comparator will always return a logical answer for every pair of values, you'll have to add the case for when both values are empty:
data.sort(function(a,b){
var anull = (a == '' || a == null), bnull = (b == '' || b == null);
if (anull && bnull)
return 0;
if (anull)
return 1;
if (bnull)
return -1;
return (parseInt(a) - parseInt(b));
})
Note that you don't need an explicit compare to both null and undefined; comparing == null is exactly the same as comparing === null and === undefined.
My making sure you tell the sort algorithm that when both values are empty they can be left alone (by returning 0), you avoid it thrashing back and forth in some weird cases.
Another thing that might speed things up would be to make a single pass through the array to convert all the empty entries to some single value (maybe null; doesn't matter) and all the non-empty entries to actual numbers. That way your sort won't be paying the price of converting the strings to numbers over and over again (that is, all the calls to parseInt()). If you want the array to be strings, you can always convert it back in a subsequent single pass.

Alternative to nested ternary operator in JS [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 months ago.
Improve this question
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 with if/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))
If you are looking to use const with a nested ternary expression, you can replace the ternary with a function expression.
const res = { distance: 1, difference: 5 };
const branch = (condition, ifTrue, ifFalse) => condition?ifTrue:ifFalse;
const word = branch(
res.distance === 0, // if
'a', // then
branch( // else
res.distance === 1 && res.difference > 3, // if
'b', // then
branch( // else
res.distance === 2 && res.difference > 5, // if
'c', // then
'd' // else
)
)
);
console.log(word);
or using named parameters via destructuring...
const branch2 = function(branch) {
return branch.if ? branch.then : branch.else;
}
const fizzbuzz = function(num) {
return branch2({
if: num % 3 === 0 && num % 5 === 0,
then: 'fizzbuzz',
else: branch2({
if: num % 3 === 0,
then: 'fizz',
else: branch2({
if: num % 5 === 0,
then: 'buzz',
else: num
})
})
});
}
console.log(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16].map(
cv => fizzbuzz(cv)
)
);
edit
It may be clearer to model it after the python if expression like this:
const res = { distance: 1, difference: 5 };
const maybe = def => ({
if: expr => {
if (expr) {
return { else: () => def };
} else {
return { else: els => els };
}
}
});
const word = maybe('a').if(res.distance === 0).else(
maybe('b').if(res.distance === 1 && res.difference > 3).else(
maybe('c').if(res.distance === 2 && res.difference > 5).else('d')
)
);
console.log(word);
edit
Another edit to remove the nested if/else branches:
const res = { distance: 1, difference: 5 };
const makeResolvedValue = def => {
const elseProp = () => def;
return function value() {
return {
if: () => ({ else: elseProp, value })
};
}
};
const value = def => ({
if: expr => {
if (expr) {
return { else: () => def, value: makeResolvedValue(def) };
} else {
return { else: els => els, value };
}
}
});
// with branching if needed
const word = value('a').if(res.distance === 0)
.else(value('b').if(res.distance === 1 && res.difference > 3)
.else(value('c').if(res.distance === 2 && res.difference > 5)
.else('d')
)
);
console.log(word)
// implicit else option for clarity
const word2 = value('a').if(res.distance === 0)
.value('b').if(res.distance === 1 && res.difference > 3)
.value('c').if(res.distance === 2 && res.difference > 5)
.else('d');
console.log(word2);
If all your truthy conditions evaluate to truthy values (so the value between the question mark and the semicolon evaluates to true if coerced to boolean...) you could make your ternary expressions return false as the falsy expression. Then you could chain them with the bitwise or (||) operator to test the next condition, until the last one where you return the default value.
In the example below, the "condsXXX" array represent the result of evaluating the conditions. "conds3rd" simulates the 3rd condition is true and "condsNone" simulates no condition is true. In a real life code, you'd have the conditions "inlined" in the assignment expression:
var conds3rd = [false, false, true];
var condsNone = [false, false, false];
var val3rd = (conds3rd[0] ? 1 : false) ||
(conds3rd[1] ? 2 : false) ||
(conds3rd[2] ? 3 : 4);
var valNone = (condsNone[0] ? 1 : false) ||
(condsNone[1] ? 2 : false) ||
(condsNone[2] ? 3 : 4);
alert(val3rd);
alert(valNone);
Your example could end up like below:
word = ((res.distance === 0) ? 'a' : false) ||
((res.distance === 1 && res.difference > 3) ? 'b' : false) ||
((res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : 'd';
As a side note, I don't feel it's a good looking code, but it is quite close to using the pure ternary operator like you aspire to do...
word = (res.distance === 0) ? 'a'
: (res.distance === 1 && res.difference > 3) ? 'b'
: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
: 'd';
This is an older question, but this is how I would do it... I would start with the default case and then change the variable or pass it unchanged as desired.
var word = 'd';
word = (res.distance === 0) ? 'a' : word;
word = (res.distance === 1 && res.difference > 3) ? 'b' : word
word = (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : word;
Sometimes we have (or just love) to use one-line expressions or variable definitions.
So, we can use a combination of destructive assignments with the ternary operator. For example,
was:
const a = props.a ? props.a : cond2 ? 'val2.0' : 'val2.1' ;
let's update to:
const { a = cond2 ? 'val2.0' : 'val2.1' } = props;
It even remains relatively well readable.
I personally love using ternary expressions for one liners.
Although, I have to agree that nesting ternary expressions can lead to sketchy code.
I started playing with the Object constructor recently to write clearer code:
let param: "one" | "two" | "three";
// Before
let before: number = param === "one" ? 1 : param === "two" ? 2 : 3;
// After
let after: number = Object({
one: 1,
two: 2,
three: 3
})[param];
Real life example:
const opacity =
Platform.OS === "android"
? 1
: Object({
disabled: 0.3,
pressed: 0.7,
default: 1,
})[(disabled && "disabled") || (pressed && "pressed") || "default"];
If you use lodash you can use _.cond
Point free version with lodash/fp:
const getWord = _.cond([
[_.flow(_.get('distance'), _.eq(0)), _.constant('a')],
[_.flow(_.get('distance'), _.eq(1)) && _.flow(_.get('difference'), _.gt(3)), _.constant('b')],
[
_.flow(_.get('distance'), _.eq(2))
&& _.flow(_.get('difference'), _.gt(5))
&& _.flow(_.get('key'), _.toString, _.gt(5)),
_.constant('c'),
],
[_.stubTrue, _.constant('d')],
]);
If you're in the mood for something a little less readable... this might be for you. Write a general function to take an array of conditions (in the order you'd write your if/else) and an array of assignment values. Use .indexOf() to find the first truth in your conditions, and return the assignment array value at that index. Order is critical, conditions need to match up by index to the assignment you want:
const conditionalAssignment = (conditions, assignmentValues) => assignmentValues[conditions.indexOf(true)];
You can modify to handle truthy instead of struct true, and beware the undefined return if indexOf is -1
I faced this too recently and a google search led me here, and I want to share something I discovered recently regarding this:
a && b || c
is almost the same thing as
a ? b : c
as long as b is truthy. If b isn't truthy, you can work around it by using
!a && c || b
if c is truthy.
The first expression is evaluated as (a && b) || c as && has more priority than ||.
If a is truthy then a && b would evaluate to b if b is truthy, so the expression becomes b || c which evaluates to b if it is truthy, just like a ? b : c would if a is truthy, and if a is not truthy then the expression would evaluate to c as required.
Alternating between the && and || trick and ? and || in the layers of the statement tricks the no-nested-ternary eslint rule, which is pretty neat (although I would not recommend doing so unless there is no other way out).
A quick demonstration:
true ? false ? true : true ? false : true ? true ? true : false : true : true
// which is interpreted as
true ? (false ? true : (true ? false : (true ? (true ? true : false) : true))) : true
// now with the trick in alternate levels
true ? (false && true || (true ? false : (true && (true ? true : false) || true))) : true
// all of these evaluate to false btw
I actually cheated a bit by choosing an example where b is always truthy, but if you are just setting strings then this should work fine as even '0' is ironically truthy.
I've been using a switch(true) statement for these cases. In my opinion this syntax feels slightly more elegant than nested if/else operators
switch (true) {
case condition === true :
//do it
break;
case otherCondition === true && soOn < 100 :
// do that
break;
}
ES6 opens the door to this, a different take on a switch statement.
Object.entries({
['a']: res.distance === 0,
['b']: res.distance === 1 && res.difference > 3,
['c']: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
}).filter(n => n[1] === true)[0][0]
I prefer inlined if-else in ternary expression
This..
let assignment;
if (loggedUser === onboard.Employee)
assignment = AssignmentEnum.Employee;
else if (loggedUser === onboard.CreatedBy)
assignment = AssignmentEnum.Manager;
else
assignment = 0;
..can be shortened to:
const assignment =
loggedUser === onboard.Employee ?
AssignmentEnum.Employee
:loggedUser === onboard.CreatedBy ?
AssignmentEnum.Manager
:
0;
Just add the following ES6 lint rules to allow the above code structure
On "indent", add this rule:
"ignoredNodes": ["ConditionalExpression"]
On "operator-linebreak", add this rule:
{ "overrides": { "?": "after", ":": "ignore" } }
But if you don't want to put the assigned value in their own line, aside from the settings above, add the multiline-ternary to off
"multiline-ternary": "off"
Here's an option that probably accomplishes the same thing, but might be verging on unreadable and liable to break. It's probably slightly less efficient than the ternary, too.
const word = [
{ value: 'a', when: () => distance === 0 },
{ value: 'b', when: () => distance === 1 && diff > 3 },
{ value: 'c', when: () => distance === 2 && diff > 5 && key.length > 5 },
{ value: 'd', when: () => 'otherwise' }, // just has to return something truthy
].find(({ when }) => when()).value
Array.prototype.find evaluates each element in the array until its callback returns a truthy value. Then it stops and returns, so we wouldn't evaluate any more cases than necessary.
We could also do it without a bunch of tiny functions. Slightly easier to read, but potentially less efficient if your calculations are slow or prone to throwing errors.
const word = [
{ value: 'a', when: distance === 0 },
{ value: 'b', when: distance === 1 && diff > 3 },
{ value: 'c', when: distance === 2 && diff > 5 && key.length > 5 },
{ value: 'd', when: 'otherwise' }, // just has to be something truthy
].find(({ when }) => when).value
But honestly, for shorter ternaries, I'd probably just go with an if/else (or remove the lint rule if you can get the rest of your team to agree).
I think some of the answers here kinda missed the point why the OP want to use ternary operator. Many prefers ternary expression as it allows them to DRY the assignment operations, not just because they can inline the returned value on same line as the condition and make the code have a lookup-like operation
If you deemed the switch(true) { is not ugly, I wish passing true is optional in javascript, either switch() { or switch {, then you can certainly DRY your assignment operations with switch:
const word =
(() => {
switch (true) {
case res.distance == 0:
return 'a';
case res.distance == 1 && res.difference > 3:
return 'b';
case res.distance == 2 && res.difference > 5 && String(res.key).length > 5:
return 'c';
default:
return 'd';
}
})();
Beside if-else and switch, you can also try square brackets.
const testName = cond1 ? [cond2 : a : b] : [cond3 ? c : d]

Categories