You Don't Know JS: Up & Going - example problem - javascript

In page 38 I found - arrays are by default coerced to strings by simply
joining all the values with commas (,) in between. You might think
that two arrays with the same contents would be == equal, but
they’re not:
var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";
a == c; // true
b == c; // true
a == b; // false
But when I run my code like the following:
var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";
console.log(typeof a == c); // false
console.log(typeof b == c); // false
console.log(typeof a == b); // false
I got different answers! Why typeof a == c and typeof b == c is false here?

its doing
typeof a == c
=> (typeof a) == c
=> "object" == c // which is false
basically typeof operator works before == sign
My cliche advice, always use paranthesis to make your intent clear
typeof (a==b) vs (typeof a) == b

The typeof operator returns the type of the content of a variable as a string. So if your variable a contains an array, typeof a will return the string "object" (as arrays are a special kind of objects).
So typeof a == c and typeof b == c compare the string "object" with the string "1,2,3". As they are obviously not the same, this returns false.
Note that the typeof operator has a higher precedence than the == operator.

Related

Add two variables in javascript efficiently

Is there an efficient way to add two variables a and b when they could be dynamic? I also need to check if their types are numbers. I think the way I have described below is not the best/modern way to implement this. Please help.
EDIT
I also want to make sure if one the variable is not a number(or null) then the variable C gets its value from the variable which is the number . How can I do it?
const a;
const b;
let c;
// variables a and b are dynamic and could be null.
if(typeOf(a)==='number' && typeOf(b)==='number'){
if(!a && b){
c=b
}
if(a && !b){
c=a
}
if(a && b){
c=a+b
}
}
You can simply check with typeof and use the logical or operator to convert NaN to 0.
if(typeof a ==='number' && typeof b === 'number'){
c = (a || 0) + (b || 0);
}
To also allow invalid values to be treated as 0, you can use this:
c = (typeof a === 'number' ? (a || 0) : 0) + (typeof b === 'number' ? (b || 0) : 0)
It could also be written like so:
c = (typeof a === 'number' && !isNaN(a) ? a : 0) + (typeof b === 'number' && !isNaN(b) ? b : 0)
I think you're looking for
const c = (typeof a == 'number' ? a : 0)
+ (typeof b == 'number' ? b : 0);

What operator checks type but not value?

So I recently learned about == and === which have been helpful, but I was wondering if there was one like this but only for types. Like it checks if they're both numbers but it doesn't matter what number they are.
You are probably looking for typeof
Ej:
var a = 1;
var b = '2';
var c = typeof b === typeof a; // c is false

Evaluate triangle by its side

My question is about using Javascript to do triangle evaluation by its sides. The following code is very initial version even it works. I'd like to know whether it could be more simplifying or there is other way to achieve the same result.
Thanks!
let a = Number(prompt('Please input the the first side (a)'))
let b = Number(prompt('Please input the the second side (b)'))
let c = Number(prompt('Please input the the third side (c)'))
if (a + b <= c || b + c <= a || c + a <= b || Number.isNaN(a) || Number.isNaN(b) || Number.isNaN(c) || a == "" || b == "" || c == ""){
console.log("invalid")
}
else if ((a > 0 && b >0 && c >0 ) && (a == b && b == c && c == a)){
console.log("equilateral triangle")
}
else if ((a > 0 && b >0 && c >0 ) && (a == b || b == c || c == a)){
console.log("isosceles triangle")
}
else {
console.log("scalene triangle")
}
Another way could be to explicitly convert the lengths to a number (0 for NaN) and sort them first. And a ternary operator can be useful here also:
let [d, e, f] = [a, b, c].map(a => +a || 0).sort((a, b) => a-b);
let result = d + e <= f ? "invalid"
: d === f ? "equilateral"
: d < e && e < f ? "scalene"
: "isosceles";
console.log(result);
This will not be the fastest when doing many thousands of them, but I like the look of it.
Explanation
[a, b, c] turns the three values into an array.
.map is a method available for arrays. For each original value in [a, b, c] the following (arrow) function is executed,
a => +a || 0
map creates a new array consisting of the results of calling that function on each separate value (so first with a, then with b and finally with c)
+a uses the unary plus as a short way of turning a value into a number, which means that you could omit the Number() calls you did in the first three lines of your code. When the result of this is NaN or 0, then || 0 will kick in: instead of NaN or 0, 0 will be taken instead (|| is a logical OR operator and 0 will only be used when the left side is considered "falsy"). This practically means that NaN is replaced by 0.
So up to now the code has roughly done something similar to the following:
let newarray = [];
newarray[0] = +a;
if (Number.isNaN(newarray[0])) newarray[0] = 0;
newarray[1] = +b;
if (Number.isNaN(newarray[1])) newarray[1] = 0;
newarray[2] = +c;
if (Number.isNaN(newarray[2])) newarray[2] = 0;
Then another array method is called on that array returned by .map(): the method .sort(). That method will use the provided callback function (a, b) => a-b to make comparisons in the array and sort it based on the values returned by such calls. It is up to the sort method to decide for which pairs this function is called. When the returned value is negative, it means the compared values are already in increasing order. When positive, they should be rearranged. When zero, they should be considered equal to the sort algorithm.
So... we now have an array that consists of numbers which are guaranteed to no longer have a NaN, and which are sorted in ascending order.
Then that array is assigned using so-called destructuring:
let [d, e, f] =
This means that the individual values of the sorted array are assigned one by one to three new variables. So this is roughly short for:
let d = new_sorted_array[0];
let e = new_sorted_array[1];
let f = new_sorted_array[2];
Because these values are now ordered, we can use them for much simpler comparisons to decide on the shape of the triangle. What follows is an expression using a chain of ternary operators which much resemble an if (...) ... else if ... chain. So:
let result = d + e <= f ? "invalid"
: d === f ? "equilateral"
: d < e && e < f ? "scalene"
: "isosceles";
... is short for this:
let result;
if (d + e <= f) result ="invalid"
else if (d === f) result = "equilateral"
else if (d < e && e < f) result = "scalene"
else result = "isosceles";
You can reduce alot of bulk and change the implementation:
You can make the user input a function call
You can place the input into an array
You can use every to make sure every value is greater than 0
You can use a new Set to remove duplicates, if the size is 1, then all sides are the same, if it's 2, then 2 sides are the same, if it's 3, all sides are different
const getSide = l => Number(prompt(`Please input the the second side (${l})`))
const sides = [getSide('a'), getSide('b'), getSide('c')]
if (sides.every(el => el > 0)) {
const size = new Set(sides).size
if (size === 1) console.log("equilateral triangle")
else if (size === 2) console.log("isosceles triangle")
else console.log("scalene triangle")
} else {
console.log("invalid")
}
Test cases:
const test = sides => {
if (sides.every(el => el > 0)) {
const size = new Set(sides).size
if (size === 1) console.log("equilateral triangle")
else if (size === 2) console.log("isosceles triangle")
else console.log("scalene triangle")
} else {
console.log("invalid")
}
}
test([0,1,2]) // invalid
test([NaN,NaN,NaN]) // invalid
test(['',1,2]) // invalid
test([3,3,3]) // eq
test([2,2,3]) // iso
test([1,2,3]) // sca

&& operator on strings in javascript

I have the following expression :
if(a && b === c) {//do something}
What would be the meaning of this condition if I consider that a,b, and c are strings ?
This is equal to if (a == true && b === c). (note that == is lose comparison. Eg. "foo" is loosely equal to true, however "" is not loosely equal to true, hence this says "if a is not an empty string, and b has the same content, and is the same type as c, then do something")).
Examples:
var a = 'foo';
var b = 'bar';
var c = 'bar';
Will be true because a is not falsy (a non empty string is truthy), and b is the same type and has the same content as c.
var a = '';
var b = 'bar';
var c = 'bar';
Will be false because a is falsy (an empty string is falsy).
var a = 'foo';
var b = 'rab';
var c = 'bar';
Will be false because b does not have the same content as c.
var a = 'foo';
var b = true;
var c = 1;
Will be false because b is not the same type as c (boolean vs number - with == this would be true, as true == 1).
Your expression expands to this:
if (a) {
if (b === c) {
// do something
}
}
It first checks that a is some truthy value. It then checks that b and c are of the same value and the same type. Read up on truthy and falsey values in JavaScript.
This means:
a is truthy
AND
b exactly equal to c (including type)
Here a is truthy means it is NOT ('undefined' or "" or 'null' or 0 or 'NaN' or 'FALSE')
if a have some value (not null and "") and b's value (string, case sensitive) is equal to c's value then your code

Testing for equality of regular expressions

I was surprised to see that
/a/ === /a/
evaluates to false in JavaScript. Reading through the specs:
Two regular expression literals in a program evaluate to regular
expression objects that never compare as === to each other even if the
two literals' contents are identical.
Since === cannot be used to test for equality, how can equality of regular expressions be tested in JavaScript?
Here's a case that even covers ordering of flags.
function regexEqual(x, y) {
return (x instanceof RegExp) && (y instanceof RegExp) &&
(x.source === y.source) && (x.global === y.global) &&
(x.ignoreCase === y.ignoreCase) && (x.multiline === y.multiline);
}
Tests:
regexEqual(/a/, /a/) // true
regexEqual(/a/gi, /a/ig) // also true.
regeXEqual(/a/, /b/) // false
Here's a function that fully tests all the relevant regex properties and makes sure it's the right type of object:
function regexSame(r1, r2) {
if (r1 instanceof RegExp && r2 instanceof RegExp) {
var props = ["global", "multiline", "ignoreCase", "source", "dotAll", "sticky", "unicode"];
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (r1[prop] !== r2[prop]) {
return false;
}
}
return true;
}
return false;
}
And, since flags sometimes get added to the regex object with new features (as has happened since this original answer in 2012 - though the above code has been updated as of 2019), here's a version that is a bit more future proof on future flags being added since it compares whatever flags are there rather than looking for a specific set of flags. It sorts the flags before comparing to allow for minor differences in how the regex was specified that wouldn't not actually change functionality.
function regexSame(r1, r2) {
return r1 instanceof RegExp &&
r2 instanceof RegExp &&
r1.source === r2.source &&
r1.flags.split("").sort().join("") === r2.flags.split("").sort().join("");
}
Compare them using toString(), and check their type too:
var a = /a/,
b = /a/;
a.toString() === b.toString() && typeof(a) === typeof(b) //true
var c = /a/,
d = /b/;
c.toString() === d.toString() && typeof(c) === typeof(d) //false
You can check the types with typeof, then toString() both regexes and compare those. It won't cover cases with equivalent flags, such as /a/gi and /a/ig, though.
function regexEquals(a, b)
{
if (typeof a !== 'object' || typeof b !== 'object') return false;
return a.toString() === b.toString();
}
Unfortunately there's no more-specific type from typeof, so if you really want to make sure they're regexes (or regex-like) you could do something along these lines:
RegExp.prototype.regexEquals = function (other)
{
return (typeof other.regexEquals === 'function')
&& (this.toString() === other.toString());
}
Then:
/a/.regexEquals(/a/); // true
/a/.regexEquals(/b/); // false
Answers above didn't consider case-sensitivity. So built upon jfriend00's answer, the function should be
function regexEqual(a, b) {
if (!(a instanceof RegExp) || !(b instanceof RegExp)) {
return false;
}
let sourceA = a.source;
let sourceB = b.source;
const flagsA = a.flags.split('').sort().join(',');
const flagsB = b.flags.split('').sort().join(',');
if (flagsA.includes('i') && flagsB.includes('i')) {
sourceA = sourceA.toLowerCase();
sourceB = sourceB.toLowerCase();
}
return sourceA === sourceB && flagsA === flagsB;
}

Categories

Resources