Javascript - Ternary Operator with Multiple Statements - javascript

Is this valid JavaScript? I saw an example where someone used commas in the ternary operator conditions, and it was marked as an error in my editor, and the example didn't run in Chrome. However, it did run in Firefox. Once I converted all the ternary statements to if/else statements, the app ran on Chrome.
a!==b ? (a=1, b=2) : (a=2, b=1)
Edit:
This is the actual statement in the code:
a!==0?b<0?(h=b/a,e=h-1,f=-2*b+2*a*e,i=-2*b+2*a*h,d=2*h*a-2*b-2*a):(h=b/a,e=h+1,f=2*b-2*a*e,i=2*b-2*a*h,d=-2*h*a+2*b):d=h=e=f=i=0

Yes, it's valid, and it runs fine in Chrome:
var a, b, c;
a = 6;
b = 7;
c = a !== b ? (a = 1, b = 2) : (a = 2, b = 1);
console.log("a = " + a);
console.log("b = " + b);
console.log("c = " + c);
I'm not saying it's a remotely good idea in code humans are meant to read. :-) I expect jamietre is correct in the comments when he/she says it looks like the result of minification.
The comma operator is a binary operator (an operator accepting two operands). It evaluates its left-hand operand (thus causing any side-effects it has, such as assignment), throws that result away, then evalutes its right-hand operand (thus causing its side-effects if any) and takes that result as its result value. If you have multiple comma operators in a row, the overall expression is evaluated in order, left-to-right, with the final result being the value resulting from the right-most operand evaluation.
And of course, you know the conditional operator (a ternary operator — one accepting three operands) is used to pick one of two sub-expressions to evaluate, on the basis of an initial expression.
So that line is very...expressive...what with a total of seven* different expressions inside it.
So in that example, the result of the overall expression is 2 if a !== b initially, or 1 if a === b initially, with the side-effects of setting a and b.
It's the side effects that make it, in my view, a questionable choice. And of course, there's no reason to use the comma operator if the left-hand operand doesn't have side effects.
* Yes, seven of 'em packed into that overall ternary:
a !== b
the first comma expression
a = 1
b = 2
the second comma expression
a = 2
b = 1
Re your edit with the actual statement, that one works too:
function test(a) {
var b = 7,
d = 1,
e = 2,
f = 3,
g = 4,
h = 5,
i = 6;
a!==0?b<0?(h=b/a,e=h-1,f=-2*b+2*a*e,i=-2*b+2*a*h,d=2*h*a-2*b-2*a):(h=b/a,e=h+1,f=2*b-2*a*e,i=2*b-2*a*h,d=-2*h*a+2*b):d=h=e=f=i=0;
console.log("a = " + a);
console.log("b = " + b);
console.log("d = " + d);
console.log("e = " + e);
console.log("f = " + f);
console.log("g = " + g);
console.log("h = " + h);
console.log("i = " + i);
}
test(0);
test(1);
.as-console-wrapper {
max-height: 100% !important;
}
But wow, I hope this is minified, because if a person wrote that, they must really have a thing against anyone who's supposed to maintain it later... ;-)

Yes:
a=1;
b=2;
a!==b ? (a=1, b=2) : (a=2, b=1)
console.log(a); // 1
console.log(b); // 2
and:
a=1;
b=2;
a===b ? (a=1, b=2) : (a=2, b=1)
console.log(a); // 2
console.log(b); // 1
As you can analyze, changing the equality operator reacts correctly to our test if you look at the results.

Or you can do this :
b = a!==b ? (a=1,2) : (a=2,1);
Read here about comma operator.
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.

Expanding on this topic with ES6 code example. If you're using one side of the TRUE : FALSE argument to iterate thru all cases in one IF, it makes sense to separate the code as if it's a switch | case statement.
Nesting implies that there is branching logic, while it is logically nested, writing nested IF's complicates what we're doing in my example. Like a lawyer over explaining a problem to a jury. IMO, you want to explain the point in it's simplest form. For instance, I find this example the most logical way of expressing nested ifs where the TRUE is executed. The final false is your last else {}
choreDoor is either 0,1 or 2:
choreDoor === 0 ?
(openDoor1 = botDoorPath,
openDoor2 = beachDoorPath,
openDoor3 = spaceDoorPath)
: choreDoor === 1 ?
(openDoor2 = botDoorPath,
openDoor1 = beachDoorPath,
openDoor3 = spaceDoorPath)
: choreDoor === 2 ?
(openDoor3 = botDoorPath,
openDoor1 = beachDoorPath,
openDoor2 = spaceDoorPath)
: false;

If you don't want to use the Comma operator (,) then you can use nested Conditional (ternary) operators instead.
var a = 6;
var b = 7;
var c = (a !== b)? // true
((a = 1 || 1===1)? (b = 2) : null) // will first run a=1, then b=2
: ((a = 0 || 1===1)? (b = 0) : null);
console.log("a = " + a);
console.log("b = " + b);
console.log("c = " + c);

Related

Is the + operator the only operator that is able to modify a string while keeping it as a string?

I'm going through this javascript course https://javascript.info/operators
I noticed that when you use operators like *, /, -, the string won't remain a string but instead will be converted. So my question leads to this. Is the plus(+) operator the only operator that is able to modify a string and afterwards it will remain a string?
No, it's not the only operator. There are many that will not type coercion. Depends on how strictly you want to define this, however, in some cases, there is a coercion happening but the end result is the original type.
Here is a list:
Unary operators
delete
The operator can remove properties from objects.
No type coercion is performed but the operation is meaningless. Calling delete on a string literal is a no-op.
"use strict";
delete "hello";
console.log("---")
Calling delete on a variable is a no-op in loose mode but throws an error in strict mode.
"use strict";
let foo = "world";
delete foo;
console.log(foo);
Grouping ()
The grouping operator doesn't change anything about the expression given. It can only change the order of evaluation, if needed. The result of the grouping operator is always the same as the expression inside it.
typeof
It returns the type of the operand given. No type coercion but the output is always a string. In the case of using typeof with primitive strings, the result is always going to be "string".
void
The void operator takes any expression and returns undefined. No type coercion is involved because whatever the value, it is just swallowed.
Binary operators
Assignment operators
Assignment is giving a value to a variable. Some of the assignment operators will not perform coercion, others do.
No coercion: =, +=, ??=, &&=, ||=
No coercion of the assigned value if it's a string. The logical assignments will only conditionally assign the value.
Example:
//`=`, `+=`, `??=`, `&&=`, `||=`
const a = "one";
let b = "t";
b += "wo";
let c = null;
c ??= "three";
let d = true;
d &&= "four";
let e = false;
e ||= "five";
console.log(a, b, c, d, e);
let x = "hello";
x ??= "twenty four";
let y = false;
y &&= "twenty five";
let z = true;
z ||= "twenty six";
console.log(x, y, z);
The addition assignment will work the same as x = x + y which also means that it could coerce to a string, if one of the operands is not that:
let foo = "four";
foo += 2;
let bar = 4;
bar += "two";
console.log(foo, bar);
Coercion is performed: *=, /=, %=, -=, <<=, >>=, >>>=, &=, ^=, |=, **=
All of these will coerce the type of the operand to a number.
Equality
No coercion: ===, !==
The strict equality/inequality operators will not perform any type coercion. If the operands are of different types, the equality check returns false.
With coercion: ==, !=
The loose equality/inequality operators will perform type coercion. How and when depends on the operands given to them. For example, with a string and a number, the string will be coerced to a number. However, with a string and an object, the object will be coerced to a primitive. If that primitive is a number, then the string operand will be coerced again.
See the loose equality algorithm for more information
const a = " 11.";
const b = 11;
console.log("a == b:", a == b);
const c = {
toString() {
return " 11.";
}
}
console.log("a == c:", a == c);
const d = {
valueOf() {
return 11;
}
}
console.log("a == d:", a == d);
const e = {
[Symbol.toPrimitive]() {
return " 11.";
}
}
const f = {
[Symbol.toPrimitive]() {
return 11;
}
}
console.log("a == e:", a == e);
console.log("a == f:", a == f);
Comma ,
The comma operator takes two expressions and returns the second one. No type coercion is involved - it's similar to the void operator in that the value of the first expression is just discarded.
Concatenation +
The addition operator only performs concatenation if at least one of the operands is a string when converted to a primitive. If the other operand does not produce a string, then it's coerced to one:
const a = "1";
const b = 2;
console.log("a + b:", a + b);
const c = {
toString() {
return "3";
}
}
console.log("a + c:", a + c);
const d = {
valueOf() {
return 4;
}
}
console.log("a + d:", a + d);
const e = {
[Symbol.toPrimitive]() {
return "5";
}
}
const f = {
[Symbol.toPrimitive]() {
return 6;
}
}
console.log("a + e:", a + e);
console.log("a + f:", a + f);
//two objects, where one converts to a string
console.log("!!! e + f !!!:", e + f);
Logical Operators ||, &&, ??
The operators will check if the value is truthy or falsy. For strings, only the empty string ("") is falsy, all other strings are truthy. However, the result of the logical operators will not change the type.
const a = true && "one";
const b = false || "two";
const c = null ?? "three";
console.log(a, b, c);
Relational
No coercion: instanceof, in
No coercion involved but some usages are meaningless.
The instanceof operator will never produce true
if the right value is a string, it throws an error, as it expects a function.
if the left value is a string, then it's not an instance of any other function, since it's a primitive.
Using a non-object as a right value of in also throws an error. It's valid as a left value and no coercion is performed.
Sometimes coercion is performed: <, >, <=, >=
These are similar to + and == in that the operands will sometimes be coerced. With two strings, there is no coercion happening (similar to + and ==) but otherwise an attempt will be made to convert to numbers. Objects are first converted to primitives.
Ternary operators
Conditional operator ? :
A string as first operand will be checked for truthyness. If it's either of the second or third operand, then it's just returned as-is based on the condition.

The different between if statement and ternary operator

function findBiggestFraction( a , b ) {
a > b ? console.log("a: ", a): ("b: ", b);
}
var firstFraction = 3/4;
var secondFraction = 5/7;
findBiggestFraction(firstFraction, secondFraction); // a: 0.75
findBiggestFraction(7/16, 13/25); // b: 0.52
findBiggestFraction(1/2, 3/4); // a: 0.75
function findBiggestFraction( a , b ) {
if (a > b) {
console.log("a: ", a);
} else {
console.log("b: ", b);
}
}
var firstFraction = 3/4;
var secondFraction = 5/7;
findBiggestFraction(firstFraction, secondFraction); // a: 0.75
findBiggestFraction(7/16, 13/25); // b: 0.52
findBiggestFraction(1/2, 3/4); // a: 0.75
when I run the first code block it only execute the first call. On the other hand for the second block, it runs all three.
You are missing console.log for b.
a > b ? console.log("a: ", a): console.log("b: ", b);
With the ternary, you're never calling console.log when the condition is false. ("b: ", b) evaluates to b, then you don't do anything with the result.
Roughly equivalent code using an if-statement would be
if (a > b) {
console.log("a: ", a);
} else {
("b: ", b);
}
The mistake is arguably much clearer when you use the more verbose statement. Change the ternary line to
a > b ? console.log("a: ", a) : console.log("b: ", b);
Note that using a ternary to run side effects is generally regarded as bad practice. Either use the ternary inside of console.log to pick what argument to pass in, or use an if-statement.
You're functions are not the same. The first function only prints something if a > b is true. The second function prints either way.
This is a caveat to using the ternary operator. It's very helpful, but can be easy to miss details like you just did.
Summary: Both blocks run both functions, but the first block only prints ~half the time.

Unambiguous increment and adding results in error

When playing around with JavaScript syntax it struck me that the following code will throw an error in SpiderMonkey and V8 engines:
var a = 1, b = 1;
a++++b;
This to me is strange, since the following works perfectly fine:
var a = 1, b = 1;
a+++b; // = 2; (Add a and b, then increase a)
// now a == 2 and b == 1
a+++-b; // = 1; (add a and -b, then increase a)
// now a == 3 and b == 1
In addition, the following would be nonsensical code:
var a = 1, b = 1;
a++ ++b; // throws an error
My argument is now that if a+++b is equivalent to a++ + b, and not to a+ ++b, and a+++-b is equivalent to a++ + -b, then a++++b can only be interpreted as a++ + +b in order for it to be valid JavaScript code.
Instead, the engines insist that a++++b is interpreted as a++ ++b, by operator precedence.
This to me is in contrast with the logic that the engines implements using the / symbol, as explained here, to distinguish between division and regular expressions. An example
var e = 30, f = 3, g = 2;
e/f/g; // == 5
e
/f/g; // == 5
/f/g; // is equivalent to new RegExp("f","g")
Here the argument is that because /f/g does not make sense as division in the last line, it is interpreted as a regular expression.
Obviously the / symbol gets a special treatment, in order to distinguish between division and regular expressions. But then why do ++ and -- not get a special treatment as well? (That is, outside operator precedence)
A second question is why operator precedence is not called only when the code is has multiple valid interpretations.
In the code a++++b you have two distinct statements: a++ and ++b with nothing to combine them. The + operator in the context of a++ + +b is actually a type converter (meant for turning strings into numbers) and has a different order of precedence which follows the others in the list.

Javascript syntax: equal signs and commas

I really didn't know what to call this question, neither what I could google for. I'm trying to understand the source code for the D3.js library and I've encountered two functions that I simply can't understand, due to the syntax that is new to me.
The first one is the number interpolator:
function d3_interpolateNumber(a, b) {
b -= a = +a;
return function(t) { return a + b * t; };
}
What's going on on the second line here? We're subtracting the value of b from the value of a and then...uhm, you lost me. How does this syntax work?
The other thing that confuses me, that I've seen in other places as well, is where the right-hand assignment of a variable consists of several variables separated by commas. As in:
var i = d3.interpolators.length, f;
What does this mean? These snippets are taken from https://github.com/mbostock/d3/blob/master/src/interpolate/number.js and
https://github.com/mbostock/d3/blob/master/src/interpolate/interpolate.js
The first line you're asking about is just two assignments. It's equivalent to this:
a = +a;
b -= a;
The +a is using the unary plus operator to convert a string to a number. So we are converting a to a number and then subtracting that number from b (and reassigning the new value to b).
The second bit of syntax you're asking about is simply a list of variable declarations. For example:
var a, b, c; // Declares 3 variables, all initialised to undefined
That's equivalent to this:
var a;
var b;
var c;
In your example, one of the declarations in the list also includes an assignment. Any number of them can, so this is valid too:
var a, b = 1, c = true, d;
An assignment is also an expression, which returns the value that is assigned. So this:
b -= a = +a;
is the same as:
b -= (a = +a);
or:
a = +a;
b -= a;
If the right hand side would really be values separated by comma, i.e:
var i = (d3.interpolators.length, f);
then the comma operator returns the value of the last operand, so it would be the same as:
d3.interpolators.length;
var i = f;
However, without the parentheses the comma is a separator between declared variables, not the comma operator, so it's the same as:
var i = d3.interpolators.length;
var f;
The second line is
b -= (a = +a);
Which means:
set a to +a (conversion to a number). Return this value outside of the parentheses.
Whatever value was returned, subtract it from b.
or
a=+a //converts a to an int
b-=a // or b=b-a
Remember, assignments return their value. So, alert(a=1) will alert 1.
On the other hand,
var i = d3.interpolators.length, f;
splits to:
var i = d3.interpolators.length;
var f;
This is just basically a way of saying "var applie to the following comma separated list"

Ordinal string compare in JavaScript?

In javascript:
"Id".localeCompare("id")
will report that "id" is bigger. I want to do ordinal (not locale) compare such that "Id" is bigger. This is similar to String.CompareOrdinal in C#. How can I do it?
I support the answers given by Raymond Chen and pst. I will back them up with documentation from my favorite site for answers to JavaScript questions -- The Mozilla Developer Network. As an aside, I would highly recommend this site for any future JavaScript questions you may have.
Now, if you go to the MDN section entitled String, under the section "Comparing strings", you will find this description:
C developers have the strcmp() function for comparing strings. In JavaScript, you just use the less-than and greater-than operators:
var a = "a";
var b = "b";
if (a < b) // true
print(a + " is less than " + b);
else if (a > b)
print(a + " is greater than " + b);
else
print(a + " and " + b + " are equal.");
A similar result can be achieved using the localeCompare method inherited by String instances.
If we were to use the string "Id" for a and "id" for b then we would get the following result:
"Id is less than id"
This is the same result that Yaron got earlier when using the localeCompare method. As noted in MDN, using the less-than and greater-than operators yields similar results as using localeCompare.
Therefore, the answer to Yaron's question is to use the less-than (<) and greater-than (>) operators to do an ordinal comparison of strings in JavaScript.
Since Yaron mentioned the C# method String.CompareOrdinal, I would like to point out that this method produces exactly the same results as the above JavaScript. According to the MSDN C# documentation, the String.CompareOrdinal(String, String) method "Compares two specified String objects by evaluating the numeric values of the corresponding Char objects in each string." So the two String parameters are compared using the numeric (ASCII) values of the individual characters.
If we use the original example by Yaron Naveh in C#, we have:
int result = String.CompareOrdinal("Id", "id");
The value of result is an int that is less than zero, and is probably -32 because the difference between "I" (0x49) and "i" (0x69) is -0x20 = -32. So, lexically "Id" is less than "id", which is the same result we got earlier.
As Raymond noted (and explained) in a comment, an "ordinal" non-locale aware compare is as simple as using the various equality operators on strings (just make sure both operands are strings):
"a" > "b" // false
"b" > "a" // true
To get a little fancy (or don't muck with [[prototype]], the function is the same):
String.prototype.compare = function (a, b) {
return ((a == b ? 0)
? (a > b : 1)
: -1)
}
Then:
"a".compare("b") // -1
Happy coding.
Just a guess: by inverting case on all letters?
function compareOrdinal(ori,des){
for(var index=0;index<ori.length&&index<des.length;index++){
if(des[index].charCodeAt(0)<ori[index].charCodeAt(0)){
return -1;
break;
}
}
if(parseInt(index)===des.length-1){
return 0;
}
return 1;
}
compareOrdinal("idd","id");//output 1
if you need to compare and find difference between two string, please check this:
function findMissingString() {
var str1 = arguments[0];
var str2 = arguments[1];
var i = 0 ;
var j = 0 ;
var text = '' ;
while(i != (str1.length >= str2.length ? str1.length : str2.length )) {
if(str1.charAt(i) == str2.charAt(j)) {
i+=1 ;
j+=1;
} else {
var indexing = (str1.length >= str2.length ? str1.charAt(i) : str2.charAt(j));
text = text + indexing ;
i+=1;
j+=1;
}
}
console.log("From Text = " + text);
}
findMissingString("Hello","Hello world");

Categories

Resources