Alternative to nested ternary operator in JS [closed] - javascript

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]

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.

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
}
}
)

Conditions with assigned variables vs inline condition check

Can't find a better Title for this question so edit suggestions will be much appreciated.
I'm wondering if there's a difference between checking condition with assigned variables and inline condition.
for example:
Option 1:
// inline conditions check
function isSomething(){
return (1 > 2 || 'a' == 'a' || 2 < 4) ||
(55 == 1 || (32 > 4 || 'a' == 'a') && 6 > 2) ||
('abc' != 'bca' && 3 == 3);
}
Option 2:
// pre assigned variables condition check
function isSomething(){
const conditionA = 1 > 2 || 'a' == 'a' || 2 < 4; // some complex condition
const conditionB = 55 == 1 || (32 > 4 || 'a' == 'a') && 6 > 2; // some complex condition
const conditionC = 'abc' != 'bca' && 3 == 3 // some complex condition
const result = conditionA || conditionB || conditionC;
return result;
}
It seems that in option 2 it must check all 3 conditions but in option 1 theoretically it can return after the first check if it's true.
Obviously option 2 is my choice as it's much more readable, though i was wondering is there a difference in behavior or performance?
Is there a way to test performance between the two options?
If you want to combine the benefits of short-circuited evaluation with readability and named variables, then
function isSomething(){
const conditionA = () => 1 > 2 || 'a' == 'a' || 2 < 4;
const conditionB = () => 55 == 1 || (32 > 4 || 'a' == 'a') && 6 > 2;
const conditionC = () => 'abc' != 'bca' && 3 == 3;
const result = conditionA() || conditionB() || conditionC();
return result;
}
As far as checking the performance I would look at jsperf.
Also please take a look at console.time(), console.profile(), and performance.now() if you haven't already.
In option 2 you are creating 3 new objects and assigning them to variables, creating object and allocating them in memory, the effect that this will have on performance would tend to be insignificant.
In option 1 if the first value is true the second option will not be evaluated as || is a short circuit operator whilst in the second option all three conditions will be evaluated regardless of the result that they return.
If performance is an issue as this method is being used multiple times I would always suggest that the performance test simulates the real world application as much as possible.

Extended Ternary expression

I know you can do ternary expressions in Javascript for an if - else statement, but how about an else- else if- else statement? I thought that surely this would be supported but I haven't been able to find any info about it and wasn't able to get it to work just hacking around.
In contrast to Robby Cornelissen's answer - there is no problems with readability if you format it properly (and not writing PHP, since it messed up the operator by making it left-associative in contrast to all other languages that have that construct):
var y =
x == 0 ? "zero" :
x == 1 ? "one" :
"other";
EDIT
What I was looking for is a shorter version of "if expression 1 is true, return expression 1. Else if expression 2 is true, return expression 2. Else return expression 3". Is there no clean way to do this?
There is: expression1 || expression2 || expression3. (It would have been nice if you had put this into your question in the first place.) This is commonly used for default values:
var defaults = null;
function hello(name) {
var displayName = name || (defaults && defaults.name) || "Anonymous";
console.log("Hello, " + displayName + ".");
}
hello("George");
// => Hello, George.
hello();
// => Hello, Anonymous.
defaults = {};
hello();
// => Hello, Anonymous.
defaults.name = "You"
hello();
// => Hello, You.
However, it is important to be aware of the conditions for truthiness. For example, if you expect "" or 0 to be a valid value that does not need to be replaced by a default, the code will fail; this trick only works when the set of possible non-default values is exactly the set of truthy values, no more and no less. E.g.
function increment(val, by) {
return val + (by || 1); // BUG
}
increment(10, 4);
// => 14
increment(10, 1);
// => 11
increment(10);
// => 11
increment(10, 0);
// => 11 <-- should be 10
In this case you need to be explicit:
function increment(val, by) {
return val + (typeof(by) === "undefined" ? 1 : by);
}
I wouldn't recommend it because of readability, but you could just nest ternary operators:
var y = (x == 0 ? "zero" : (x == 1 ? "one" : "other"));
This would be the equivalent of:
var y;
if (x == 0) {
y = "zero";
} else if (x == 1) {
y = "one";
} else {
y = "other";
}
You can extend a ternary condition if you're good. It gets to be messy though.
var number = 5;
var power = 2;
var ans = Math.pow(number,power);
var suggest = ( ans == 5 ? 5 : ans == 10 ? 10 : ans == 15 ? 15 : ans == 25 ? "works" : null);
console.log(suggest);
I may have added to many because I'm on my phone haha but try it in your developer panel.

A quick way to test equality of more than 2 values at once?

I was wondering if there was a quick way to test the equality of more than two values in js. Something similar to (= 6 6 6).
In the console, I tried things like...
1 == 1 == 1 == 1
true
2 == 2 == 2 == 2
false
0 == 0 == 0
false
0 == 0 == 0 == 0
true
...which was amusing, but also puzzling.
Is there a quick way of doing this in js?
Thanks.
The reason you got unexpected behavior is because we need to adjust your expectations in js a bit ;) 2 == 2 == 2 == 2 does 3 comparisons, all from left to right. The first comparison is the leftmost 2 == 2, which evaluates to true. After that we get the result of the first comparison being compared to (what is in this case) the 3rd 2. Ie, true === 2, which is false. And finally, we get false === 2, which is also false.
It might help to visualize it as such:
(((2 == 2) == 2) == 2)
I think in general a === b && b === c might be what you're looking for.
EDIT: Ah, and sorry I keep switching out the == for ===. It's just habit. And it's a habit I'd recommend. the === operator doesn't do type casting, so it evaluates the value proper, not a casted version of the value.
It's because true == 1 but true != 2
You can try:
function isEquals() {
var flag = true;
for(var i=1; i<arguments.length; i++) flag = flag && (arguments[i] == arguments[0]);
return flag;
}
isEquals(2,2,2); // true
or:
function isEquals() {
var ar = arguments;
return Array.prototype.every.call(arguments, function(a){return a==ar[0];});
}
Yes you can, but you need to use the "Logical Operators" like the && or || to check more than 1 statement like (x<1 && y>0).
You can use this as a quick easy reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators
If you have more than three values, it might be more convenient to create a function for use on an array:
function allEqual(arr) {
return arr.every(function (x, i) {
return i === 0 || x === arr[i - 1];
});
}
allEqual([1, 1, 1])
ES6:
function allEqual(...arr) {
return arr.every((x, i) => i === 0 || x === arr[i - 1]);
}
allEqual(1, 1, 1)
As an addition to #vp_arth's answer you could even add a method to the Array prototype
Array.prototype.isHomogeneous = function(){
return Array.prototype.every.call(this, function(c,i,a){ return c === a[0];})
}
So you could do
[1,2,3].isHomogeneous() = false
[1,1,1].isHomogeneous() = true

Categories