Why is `exp && "t" || "f"` much slower than inline-if-else? - javascript

Why is the logical expression twice slower than if-else or inline-if-else?
function logicalExp(val) {
return val && "t" || "f";
}
function inlineIfElse(val) {
return val ? "t" : "f";
}
function ifElse(val) {
if (val) return "t";
else return "f";
}
All functions evaluate with same results.
All functions are being passed a value from an array of 1 and 0, see this jsperf test.

Because it does need to evaluate whether "t" is truthy or not. The short-circuit expression return ((val && "t") || "f") can be expanded to
var and = val ? "t" : val;
var or = and ? and : "f";
return or;
Of course, an optimising compiler could statically determine the truthiness of the "t" literal, and avoid doing ToBoolean(val) twice, but apparently this is not done in any JS engine.

Because
val && "t" || "f"
has to evaluate val and, if val evaluates to true, "t" as well.
Using only false is therefore significantly faster than only true, but still quite slow.

Related

javascript string expression as if condition

I am building a string which is to be evaluated as if condition, like
if (location_val.length == 1) {
condition = condition + " v.location === '" + location_val + "' &&";
} else {
for (var i = 0; i < location_val.length; i++) {
condition = condition + " v.location === '" + location_val[i] + "' && ";
}
}
but javascript evaluates the variable if it is not null, undefined, 0 or empty string, how to evaluate string as expression? I am using condition variable as follows, but it always returns true because of the above reason.
if(condition)
Build your expression properly - not using string concatenation, its something like this:
var condition = (location_val.length == 1 && v.location == location_val)
|| (location_val.every(function(loc){ return v.location === loc; } );
if(condition) {
....
}
I suspect there is more to this (you have && at the end of both strings) but the same applies to everything else. Boolean's can be combined in parts
var condition1 = (location_val.length == 1 && v.location == location_val)
|| (location_val.every(function(loc){ return v.location === loc; } );
var condition2 = ...
var condition3 = ...
if(condition1 && condition2 && condition3){
....
}
In JavaScript there's the eval function. You can use it to evaluate a string as an expression.
eval('3 + 2 === 5') //true
I would recommend you to rethink your solution, though, since working with strings as expressions when you could solve your problem differently, is really error prone and literally the worst practise.
Also use === instead of ==
(=== works as you'd expect == to work, == doesn't)

Why does charAt not detect a string?

Why does the following function return a vowel at index 2 when, index 2 is NOT a vowel?
function isVowel(name) {
console.log("The third letter of " + name + " " + "is " + name.charAt(2))
if (name.charAt(2) === "a" || "i" || "o" || "u")
console.log("3rd letter is vowel")
else
console.log("3rd letter is NOT vowel")
}
isVowel("abcdefg")
/*Outputs:*/ The third letter of abcdefg is c
3rd letter is vowel
In JavaScript (and all the other languages with similar syntax), this line:
if (name.charAt(2) === "a" || "i" || "o" || "u")
means
if name.charAt(2) === "a"
or "i"
or "o"
or "u"
it does not mean
if name.charAt(2) === "a"
or name.charAt(2) === "i"
or name.charAt(2) === "o"
or name.charAt(2) === "u"
In a lot of languages you'd get an error because "i" isn't a boolean value, so || "i" is an odd thing to say; but JavaScript is happy to do type coercion, and so false || "e" results in true because "e" is a "truthy"1 value.
To make it mean what you want it to mean, you have to repeat the left-hand operand:
if (name.charAt(2) === "a" ||
name.charAt(2) === "i" ||
name.charAt(2) === "o" ||
name.charAt(2) === "u")
You might want to use a variable to avoid repeatedly calling charAt, or look at doing something else, like this typical "is X in Y" approach:
if ("aiou".indexOf(name.charAt(2) !== -1)
Side note: Aren't you missing "e" (and sometimes "y")? ;-)
1 "truthy value" - Values that coerce to true when used as booleans are truthy; ones that coerce to false are "falsy." The falsy values are 0, "", NaN, null, undefined, and of course, false; all other values are truthy.
A non-empty string is treated as a truthy value in JS so the if statement would be always true. If the first condition name.charAt(2) === "a" fails it will check the second condition "i" which would be always treated as truthy since it's non-empty string.
Instead, you can do something simple like this using String#indexOf method.
if ("aiou".indexOf(name.charAt(2)) > 1)
Change the if condition to
name.charAt(2)==='a' || name.charAt(2)==='i' || name.charAt(2)==='o'||name.charAt(2)==='u'
The || operator does not work like that:
if (name.charAt(2) === "a" || "i" || "o" || "u")
is syntactically correct but it won't do what you expect. You need to do a separate comparison to each vowel. Alternatively, you can keep the vowels in a string and check by search or lookup:
if ("aeiou".indexOf(name.charAt(2)) >= 0)
or
if ("aeiou".includes(name.charAt(2)))
(The .includes() function used in the latter example doesn't have widespread support yet.)
You need to check every letter with a single comparison.
function isVowel(name) {
console.log("The third letter of " + name + " " + "is " + name.charAt(2))
if (name.charAt(2) === "a" || name.charAt(2) === "i" || name.charAt(2) === "o" || name.charAt(2) === "u") {
console.log("3rd letter is vowel");
} else {
console.log("3rd letter is NOT vowel");
}
}
isVowel("abcdefg");
A shorter ocde could be to check a string with the vowels and get the position of the letter, to check against.
function isVowel(name) {
console.log("The third letter of " + name + " " + "is " + name.charAt(2))
if ('aeiou'.indexOf(name[2]) !== -1) {
console.log("3rd letter is vowel");
} else {
console.log("3rd letter is NOT vowel");
}
}
isVowel("abcdefg");
You can use an Object as a hash map to check in constant time if a character is a vowel (your vowel check condition was wrong, it is always returning true)
var vowels = {
a: true,
i: true,
e: true,
o: true,
u: true
}
if(name.charAt(2) in vowels) {
...
}
Why is your condition always returning true?
Because those are all equivalent in your case:
if (name.charAt(2) === "a" || "i" || "o" || "u")
if ((name.charAt(2) === "a") || ("i" || "o" || "u"))
if ((name.charAt(2) === "a") || true)
if (THIS_CAN_BE_ANYTHING || true)
if (true) // So, your condition is always true

How to catch null, undefined, blank values with AngularJS filter

I'm attempting to write a filter for use in a grid that will catch all null, undefined, blank string, or other similar values and display a dash "-". I've written the following so far, but it doesn't catch null values, and I'm wondering if it could be more succinct and possibly refactored to avoid three layers of nested if/else statements. Percentage values need to be checked that they're over 0 and under 1. Also, negative numbers and 0's should be returned as is. Thanks!
angular.module('AdverseEventsExplorer.main').filter('emptyCellFilter', function ($filter) {
return function (input, cellFilter, args1, args2) {
if (cellFilter == undefined) {
return (angular.isNumber(input) || angular.isDefined(input) && input.length > 0) ? input : '-';
} else {
if (cellFilter.match(/pctg|percent|pctgFilter|incidence/ig)) {
return (input > 0 && input < 1.0000000) ? $filter(cellFilter)(input, args1, args2) : '-';
} else {
return (angular.isNumber(input) || angular.isDefined(input) && input.length > 0) ? input : '-';
}
}
};
});
Version 2.0 taking into account #tymeJV's comment:
angular.module('AdverseEventsExplorer.main').filter('emptyCellFilter', function ($filter) {
return function (input, cellFilter, args1, args2) {
if (!cellFilter) {
return (angular.isNumber(input) || (input)) ? input : '-';
} else {
if (cellFilter.match(/pctg|percent|pctgFilter|incidence/ig)) {
return (input > 0 && input < 1.0000000) ? $filter(cellFilter)(input, args1, args2) : '-';
} else {
return (angular.isNumber(input) || (input)) ? $filter(cellFilter)(input, args1, args2) : '-';
}
}
};
});
Whenever you encounter a function that's getting too complex to refactor try extracting some of the smaller statements to concisely named variables. It makes it much easier for our brains to keep track of the function's requirements, and it's also more readable to new devs reading your code.
var inputHasValue = angular.isNumber(input) || input;
if(!inputHasValue){
return '-';
}
if (!cellFilter) {
return input;
}
var isPercentageCell = cellFilter.match(/pctg|percent|pctgFilter|incidence/ig);
var valueIsInRange = input > 0 && input < 1;
if(!isPercentageCell || valueIsInRange){
return $filter(cellFilter)(input, args1, args2);
}
return '-';
typeof x ==='number' || !!x
is false when x is null, undefined or empty string
Only one case in which it doesn't work – if you need to filter boolean variables, but your case doesn't seem to need it.
Anyway in that case you can use
typeof x === 'boolean' || typeof x ==='number' || !!x

Surprising value assignment due to operator precedence?

Original question based on completely wrong logic :O
$(function() {
var x=0,y=1;
$("#div_id").find("input:text").each(function (i, input) {
$(this).val(""+(i % 2 == 0)?x++:y++);
});
});
If I remove the ""+ from the value, I get this wrong result
instead of what I want, which is this:
Same if I use prop:
$(this).prop("value",""+(i % 2 == 0)?x++:y++);
Same if I prefix the ++
What am I overlooking? Is this an obvious thing?
UPDATE: I completely missed the boat here.
Here is the code I meant to write and it works without the ""
$(function() {
var x=0,y=1;
var inputs = $("#div_id").find("input:text");
var y = Math.ceil(inputs.size()/2);
inputs.each(function (i, input) {
$(this).prop("value",(i % 2 == 0)?++x:++y);
});
});
"" + (i % 2 == 0) ? x++ : y++
is interpreted as (see precedence rules)
("" + (i % 2 == 0)) ? x++ : y++
and ("" + (i % 2 == 0)) is always truthy, because both "true" and "false" are non-empty strings. So in your example, you're using x++ all the time, ignoring the y++ branch.
It seems the associativity of + operator which is from left to right of expression. If you remove ""+ only (i % 2 == 0) makes condition part of conditional operator which will be evaluated to true of false. but if you include it will not evaluated to boolean rather string and would be true always.
With string you will always get true
alert("" + false ? "true" : "false");
Live Demo

Explain a block of crazy JS code inside Sizzle(the CSS selector engine)

So, here is the function for pre-filtering "CHILD":
function(match){
if ( match[1] === "nth" ) {
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
// calculate the numbers (first)n+(last) including if they are negative
match[2] = (test[1] + (test[2] || 1)) - 0;
match[3] = test[3] - 0;
}
// TODO: Move to normal caching system
match[0] = done++;
return match;
}
The code is extracted from line 442-458 of sizzle.js.
So, why is the line var test = ..., have the exec inputing a boolean? Or is that really a string?
Can someone explain it by splitting it into a few more lines of code?
The exec method will receive a string, because the Boolean Logical Operators can return an operand, and not necessarily a Boolean result, for example:
The Logical AND operator (&&), will return the value of the second operand if the first is truthy:
true && "foo"; // "foo"
And it will return the value of the first operand if it is by itself falsy:
NaN && "anything"; // NaN
0 && "anything"; // 0
The Logical OR operator (||) will return the value of the second operand, if the first one is falsy:
false || "bar"; // "bar"
And it will return the value of the first operand if it is by itself non-falsy:
"foo" || "anything"; // "foo"
Falsy values are: null, undefined, NaN, 0, zero-length string, and of course false.
Anything else, evaluated in boolean context is truthy (will coerce to true).
So, let's look the expression:
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
match[2] === "even" && "2n" || // return '2n' if match[2] is 'even'
match[2] === "odd" && "2n+1" || // return '2n+1' if it's 'odd'
!/\D/.test(match[2]) && "0n+" + match[2]|| // return '0n+N' if it's a digit(N)
match[2] // otherwise, return the match[2] value
);

Categories

Resources