Fastest way to perform this? Ternary Operator? Switch? Array? - javascript

What is the fastest way to perform this? The vars in the parens on the left return boolean and they represent window size ranges. (e.g. o1281 returns true for screens 1281 and up, o1025 returns true for 1025 and up, etc.)
markup = // ternary triangle (the rows set the markup priority at each band)
( o1281 ) ? r1281 || r1025 || r961 || r641 || r481 || r320 || r0 || omarkup:
( o1025 ) ? r1025 || r961 || r641 || r481 || r320 || r0 || omarkup:
( o961 ) ? r961 || r641 || r481 || r320 || r0 || omarkup:
( o641 ) ? r641 || r481 || r320 || r0 || omarkup:
( o481 ) ? r481 || r320 || r0 || omarkup:
( o320 ) ? r320 || r0 || omarkup:
( o0 ) ? r0 || omarkup:
omarkup;
I was thinking maybe break it into 2 ranges based on the middle (o641) condition.
Is it worth it?

Okay so what you are doing is looking for the first truthy o-value going in descending order, and once you find one you are looking for the first truthy r-value whose name is <= this o-value. Finding none, you wish to return the value omarkup.
Because you only have a few o-values and r-values, your code, as "interesting" as it may be, appears to make the least number of tests possible.
I can't see how switches or hashes would help, since you do appear to be searching in descending order for the first truthy value.... Because of this, I don't see why splitting would help performance either. Leave it as is, or, if you prefer readability (as many programmers do), make objects for which 1281, 1025, etc. are fields.
Also, worrying about performance is usually something one does when faced with an operation that will executed many, many times. Is this the case here? This looks like something you would only execute once, assuming the o and r values don't change. (Just an FYI.)
ADDENDUM
Based on the comment added to the question above, it looks like an operation you would like to execute multiple times. In this case it probably okay to self-optimize at the JavaScript level, although modern compilers are pretty good. The biggest concern from a code-review perspective would be that the values 1281, 1025, 961, 941, and so on are laid out manually and are part of the source code, so maintenance, such as adding new size-values here is, well, tricky and error-prone. That said, based on what you have shown, I think it is safe to say your JavaScript as written is the best obtainable given a naive compiler. You can always shoot for an implementation that defines these values exactly once and then profile it to see if it is "fast enough". For example you can define an array such as
var sizes = [1281,1025,961,641,481,320,0]
and loop through, but yes, there is overhead in such implementations.
One issue that might help you here is to consider carefully what can and cannot be cached. This could help speed up future executions of this code.

Related

Specific case of using logical vs ternary operators in JavaScript

I came across this expression in Angular documentation:
(name && name.trim()) || '<no name set>';
This is basically the same as
name ? name.trim() : '<no name set>';
I've tested this for the following inputs for name and both expression result in same:
function fn1(name){
return (name && name.trim()) || '<no name set>';
}
function fn2(name){
return name ? name.trim() : '<no name set>';
}
var inputs = [undefined, null, '','astring']
console.log(inputs.map(fn1));
console.log(inputs.map(fn2));
So the question using which one of the above is a better practice. I guess the ternary one is more readable. However, I appreciate your thoughts about this or anything that I might have missed.
UPDATE
I missed one test, which is an all white-space string. This results in different outputs.
function fn1(name){
return (name && name.trim()) || '<no name set>';
}
function fn2(name){
return name ? name.trim() : '<no name set>';
}
var inputs = [' ','\t']
console.log(inputs.map(fn1));
console.log(inputs.map(fn2));
As suggested by #T.J. Crowder this question can be framed in a less-opinion based form like:
Will (x && x.y) || z ever give a different result from x ? x.y : z?
But what I learned from this framing is more important. When you see a code like one in Angular doc, if you think of it in terms of something more general like above, you can see the difference better. Looking at the new question, it's easy that you see x.y might evaluate to false just like x. And because all values that evaluate to false are not the same because we have empty strings, undefined, etc., then the two expressions are not equal.

Javascript printing all if conditions in textbot [duplicate]

This question already has answers here:
What's the prettiest way to compare one value against multiple values? [duplicate]
(8 answers)
Closed 4 years ago.
This is literally the first thing I have ever coded and I am self-taught, so I'm sure it's a mess and I've made a dumb error, but after an hour of looking up the issue and trying things I cannot solve this.
To try and teach myself very basic javascript concepts I've written a textbot that has existential conversations with you. The problem is that regardless of user input, all the if statements for the condition of whatDream are printing in a row. Can anyone tell me what I've screwed up here? Apologies if my methods are wack - 100% of what I'm doing is from self teaching and letting myself flail to learn, and I'll appreciate any advice.
function yesHappy (){
alert ("Good for you. To be happy as a human is a great gift.");
alert ("The content human starts over again everyday, in spite of all they know, against all they know.");
alert ("If one advances each day confidently in the direction of their dreams, and endeavors to live the life which they have imagined, they will meet with a success unexpected in common hours.");
var g = 0
while (g < 2) {
g = 0
var whatDream = prompt ("What is your dream?");
if (whatDream === "success" || "Success" || "power" || "Power" || "wealth" || "Wealth"){
alert ("That is a goal that takes much will in the earthly domain, and is not one I can readily reccomend.");
alert ("But, read your fate, see what is before you, and walk into futurity.");
g = 3
}
if (whatDream === "friends" || "Friends" || "family" || "Family" || "love" || "Love"){
alert ("To surround oneself with others fulfills a basic human goal to be social, thus your dream is only natural.");
alert ("Properly speaking, a human has as many social selves as there are individuals who recognize them.");
alert ("To all of these you surround yourself with, see in your goals only to show your true social self.");
g = 3
}
if (whatDream === "fun" || "Fun" || "charity" || "Charity" || "faith" || "Faith" || "travel" || "Travel"){
alert ("These are honorable dreams, chosen to bring joy to yourself and others.");
alert ("Above all however, avoid falsehood, especially falseness to yourself.");
alert ("Be true to your purpose.");
g = 3
}
if (g === 0){
alert ("Though knowledge is the plague of life, and consciousness an open wound in its heart, I am still sad to say I don't know what you mean.");
alert ("Let's try that again.");
}
}
}
You need to refactor all your conditional statements. For example: This one
if (whatDream === "success" || "Success" || "power" || "Power" || "wealth" || "Wealth")
Would have to be translated to:
if (whatDream === "success" || whatDream === "Success" || whatDream === "power" || whatDream === "Power" || whatDream === "wealth" || whatDream === "Wealth")
Which does what you want: Checks to see if whatDream equals any of those. Or alternatively you can use a data structure and use a method to check if the string exists in that data structure- but above is the simplest refactoring you'd have to do.
Javascript has "truthy" and "Falsey" values. Strings like "Success" map to true whereas "" map to false. So your conditionals were always evaluating to true because strings with length > 0 are "truthy". This is probably more explanation that you need- but its important to know that in the future of javascript development.
In essence- your issue is that you weren't checking the values of whatDream in your boolean logic. The above simple refactoring I did fixes it.
If you're curious: You can read up on truthy and falsey values here:
https://j11y.io/javascript/truthy-falsey/

Best way to refactor complex if expression [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 6 years ago.
Improve this question
I have just come on board to a big e-commerce project that has an angularJS front-end. I have been tasked with adding a lot of complex features to the checkout pages... which already have a lot of complex logic.
To make things harder I keep coming across lots of if statement expressions like the one below which are making it hard to understand and it is a slow process going through this code with many of these type of if expressions.
Some of these expressions are pretty critical and are sometimes even longer... There are no unit tests and when I ask other devs what this is checking for and why (just to be sure I understand) I usually get pointed to someone else rather than an explanation.
if ((!previousBillingAddress) || (previousBillingAddress && previousBillingAddress.id !== bag.paymentAddress.id)){
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
Does anyone have a good tip for refactoring these types of expressions?
I thought of breaking them down into functions that have descriptive names and the functions could return true or false but Im not sure if there is a better way.
Let there be
A = previousBillingAddress
B = previousBillingAddress.id !== bag.paymentAddress.id
then your expression is:
if (!A || (A && B)) {
log1
} else {
log2
}
What we can do we with !A || (A && B)? It's equal to !A || B:
A | B | !A | A && B | !A || (A && B) | !A || B
==========================================================
1 | 1 | 0 | 1 | 1 | 1
1 | 0 | 0 | 0 | 0 | 0
0 | 1 | 1 | 0 | 1 | 1
0 | 0 | 1 | 0 | 1 | 1
That's why your expression is equal to:
if (!previousBillingAddress || previousBillingAddress.id !== bag.paymentAddress.id) {
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
TL;DR
Above table is only check if !A || (A && B) is equal to !A || B. How to guess !A || B? In case of such expressions it's good to play with following rules:
A == !(!(A)) (rule 1)
!(A && B) == !A || !B (rule 2)
!(A || B) == !A && !B (rule 3)
A && (B || C) == A && B || A && C (rule 4)
So we have !A || (A && B), let's play. Due to rule 1 it's equal to
!(!(!A || (A && B)))
Now we use rule 3:
!(!(!A || (A && B))) == !(A && !( A && B))
Rule 2:
!(A && !( A && B)) == !(A && (!A || !B)) (*)
Due to rule 4:
A && (!A || !B) == (A && !A) || (A && !B)
We have (A && !A) || (A && !B) and it can be reduce to (A && !B). Now we can back to (*) and we have:
!(A && (!A || !B)) == !((A && !A) || (A && !B)) == !(A && !B)
With rule 2 we got:
!(A && !B) == !A || B
You can drop the previousBillingAddress && part - in the second operand of the || you already have estblished that previousBillingAddress is not falsy. That would make the overall condition
if (!previousBillingAddress || previousBillingAddress.id !== bag.paymentAddress.id) {
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
which seems short enough for me. If not, make an appropriately named helper function to which you pass the previousBillingAddress and the bag.
In my personal opinion, i think you can refactor and encapsulate the validation/check inside of a function, then if the same validation applies to other part of your code (file/module/etc..), you can reuse it. In this case you are using angular, so might be good to use an angular custom service for this type of business validations, since they also can manipulate the scope
Comments are our best friend. You want to make sure that the next person to take a look at your code knows exactly what it is supposed to do and from that, it will be quicker and easier to see exactly what it is doing.
In this case the expression in the if statement can be shortened. This is how I would comment it with almost a pseudocode approach:
// we need to check if the user has, and is using, a previous billing address
if (previousBillingAddress && previousBillingAddress.id === bag.paymentAddress.id)){
// the user is using a previous billing address
// do some work with the previous billing address
} else {
// the user is using a new address
// do some work with the new address
// save the new billing address
}
Note that previousBillingAddress is the main subject of this if statement. If we are checking for previousBillingAddress then in our first block we want to make sure we deal with what happens if we have a previous billing address. It's more logical that way.
It makes a lot less sense to check if we don't have a previous billing address and then what do we do if we have one.
Look how clean and logical it is! <3
I recommend hoisting the complex conditionals into Boolean variables (or constants, if your supported level of JS allows you to use const). For example:
var weakPrevBillingAddress =
!previousBillingAddress ||
(previousBillingAddress &&
(previousBillingAddress.id !== bag.paymentAddress.id));
if (weakPrevBillingAddress) {
console.log('judged weak previousBillingAddress');
// other processing expected here
} else {
console.log('judged strong previousBillingAddress');
// other processing expected here
}
As you and philip yoo suggest, you could hoist the conditional into a Boolean helper function. But most of the ways of defining such functions will put the logic farther away from the point of use, making comprehension harder. Boolean variables (a.k.a. "predicates") can be easily defined close to their use (e.g. right before your if statement). So there isn't much additional distance, but they still separate the computation of the Boolean value from its use. Your ability to focus on the computation separately can make things simpler and more comprehensible. As shown above, it also allows you to split long
lines and use indentation for better clarity.
I don't know if you have the flexibility to rename existing variable names. If you do, the ones you're using are exceedingly long. Long names can help increase understanding, but a 22-character name repeated several times per line is distracting, not clarifying. Shorter names, either for the source values, or just for the predicates you compute, can help. E.g.:
var weakPrevBA = !prevBA ||
(prevBA.id !== bag.paymentAddress.id);
Choosing good variable names is an art, and developer tastes about "how much description is enough" vary. But in an app that deals with billing addresses all the time, line after line after line, spelling out billingAddress every time doesn't necessarily help. If you're reading the code and comments, you probably are highly aware you're dealing with billing addresses. If not, add a quick comment, rather than spell out the full concept multiple times per line.
Note that I've also simplified the Boolean expression in this shorter example. (This was also suggested by Bergi) That can be a good idea--as long you're sure it can be simplified correctly. However, do this only with great caution. You don't have unit tests, so you have no easy way to test if simplifications are truly identical. And you're dealing with complex code. I recommend keeping the original Boolean expressions, at least to start (possibly with shortened names). That reduces one place bugs can creep in, at least until you understand the code more fully. The better you understand the code, and the simpler you have made it (e.g. by adding computed predicates, rather than long, complex expressions inside conditionals), the more freedom and safety you have in rewriting expressions.
Something like this
var billingId = previousBillingAddress && previousBillingAddress.id;
if (billingId || billingId === bag.paymentAddress.id){
console.log('all of the above was false'); // just a dummy log
} else {
console.log('all of the above was true'); // just a dummy log
}
what i did
since you are using if and else its not a good practice to use negation in the if statement. so i removed that
if you only want the negated part (without an else)
if (!(billingId || billingId === bag.paymentAddress.id)){
console.log('all of the above was true'); // just a dummy log
}
the && will return the first false, or the last true value. so billingId will equal false or the id. building upon that, your first if is checking if its "falsey" continue, so you can use the same variable as well.

Checking for null vs using || when getting length

I have been working on a project and I often have to get the length of a variable. I recently started using the following syntax because I think it looks better and is faster to type, especially when I have long variable names and multiple conditions.
if ((myArrayVariable || "").length > 0) {
// Do stuff...
}
Previously, I did it like this:
if (myArrayVariable != null && myArrayVariable.length > 0) {
// Do stuff...
}
What I want to know is, if/(how much) the first syntax is less efficient?
To test the performance, I made a jsperf test that test null, an empty array, and an array with one element on the following variants:
The first style (array || '').length > 0
The second syntax array != null && array.length > 0
The third version array && array.length
The initial run indicates that the first variation is 96% slower than the others. Additionally the other two are nearly the same, though adeneo's suggestion is about half a percent faster than the second.

How to shorten my conditional statements

I have a very long conditional statement like the following:
if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
// do something.
}
I was wondering if I could refactor this expression/statement into a more concise form.
Any idea on how to achieve this?
Put your values into an array, and check if your item is in the array:
if ([1, 2, 3, 4].includes(test.type)) {
// Do something
}
If a browser you support doesn't have the Array#includes method, you can use this polyfill.
Short explanation of the ~ tilde shortcut:
Update: Since we now have the includes method, there's no point in using the ~ hack anymore. Just keeping this here for people that are interested in knowing how it works and/or have encountered it in other's code.
Instead of checking if the result of indexOf is >= 0, there is a nice little shortcut:
if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
// Do something
}
Here is the fiddle: http://jsfiddle.net/HYJvK/
How does this work? If an item is found in the array, indexOf returns its index. If the item was not found, it'll return -1. Without getting into too much detail, the ~ is a bitwise NOT operator, which will return 0 only for -1.
I like using the ~ shortcut, since it's more succinct than doing a comparison on the return value. I wish JavaScript would have an in_array function that returns a Boolean directly (similar to PHP), but that's just wishful thinking (Update: it now does. It's called includes. See above). Note that jQuery's inArray, while sharing PHP's method signature, actually mimics the native indexOf functionality (which is useful in different cases, if the index is what you're truly after).
Important note: Using the tilde shortcut seems to be swathed in controversy, as some vehemently believe that the code is not clear enough and should be avoided at all costs (see the comments on this answer). If you share their sentiment, you should stick to the .indexOf(...) >= 0 solution.
A little longer explanation:
Integers in JavaScript are signed, which means that the left-most bit is reserved as the sign bit; a flag to indicate whether the number is positive or negative, with a 1 being negative.
Here are some sample positive numbers in 32-bit binary format:
1 : 00000000000000000000000000000001
2 : 00000000000000000000000000000010
3 : 00000000000000000000000000000011
15: 00000000000000000000000000001111
Now here are those same numbers, but negative:
-1 : 11111111111111111111111111111111
-2 : 11111111111111111111111111111110
-3 : 11111111111111111111111111111101
-15: 11111111111111111111111111110001
Why such weird combinations for the negative numbers? Simple. A negative number is simply the inverse of the positive number + 1; adding the negative number to the positive number should always yield 0.
To understand this, let's do some simple binary arithmetic.
Here is how we would add -1 to +1:
00000000000000000000000000000001 +1
+ 11111111111111111111111111111111 -1
-------------------------------------------
= 00000000000000000000000000000000 0
And here is how we would add -15 to +15:
00000000000000000000000000001111 +15
+ 11111111111111111111111111110001 -15
--------------------------------------------
= 00000000000000000000000000000000 0
How do we get those results? By doing regular addition, the way we were taught in school: you start at the right-most column, and you add up all the rows. If the sum is greater than the greatest single-digit number (which in decimal is 9, but in binary is 1) we carry the remainder over to the next column.
Now, as you'll notice, when adding a negative number to its positive number, the right-most column that is not all 0s will always have two 1s, which when added together will result in 2. The binary representation of two being 10, we carry the 1 to the next column, and put a 0 for the result in the first column. All other columns to the left have only one row with a 1, so the 1 carried over from the previous column will again add up to 2, which will then carry over... This process repeats itself till we get to the left-most column, where the 1 to be carried over has nowhere to go, so it overflows and gets lost, and we're left with 0s all across.
This system is called 2's Complement. You can read more about this here:
2's Complement Representation for Signed Integers.
Now that the crash course in 2's complement is over, you'll notice that -1 is the only number whose binary representation is 1's all across.
Using the ~ bitwise NOT operator, all the bits in a given number are inverted. The only way to get 0 back from inverting all the bits is if we started out with 1's all across.
So, all this was a long-winded way of saying that ~n will only return 0 if n is -1.
You can use switch statement with fall thru:
switch (test.type) {
case "itema":
case "itemb":
case "itemc":
case "itemd":
// do something
}
Using Science: you should do what idfah said and this for fastest speed while keep code short:
THIS IS FASTER THAN ~ Method
var x = test.type;
if (x == 'itema' ||
x == 'itemb' ||
x == 'itemc' ||
x == 'itemd') {
//do something
}
http://jsperf.com/if-statements-test-techsin
(Top set: Chrome, bottom set: Firefox)
Conclusion :
If possibilities are few and you know that certain ones are more likely to occur than you get maximum performance out if || ,switch fall through , and if(obj[keyval]).
If possibilities are many, and anyone of them could be the most occurring one, in other words, you can't know that which one is most likely to occur than you get most performance out of object lookup if(obj[keyval]) and regex if that fits.
http://jsperf.com/if-statements-test-techsin/12
i'll update if something new comes up.
If you are comparing to strings and there is a pattern, consider using regular expressions.
Otherwise, I suspect attempting to shorten it will just obfuscate your code. Consider simply wrapping the lines to make it pretty.
if (test.type == 'itema' ||
test.type == 'itemb' ||
test.type == 'itemc' ||
test.type == 'itemd') {
do something.
}
var possibilities = {
"itema": 1,
"itemb": 1,
"itemc": 1,
…};
if (test.type in possibilities) { … }
Using an object as an associative array is a pretty common thing, but since JavaScript doesn't have a native set you can use objects as cheap sets as well.
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }
or if the items are not that uniform, then:
if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }
Excellent answers, but you could make the code far more readable by wrapping one of them in a function.
This is complex if statement, when you (or someone else) read the code in a years time, you will be scanning through to find the section to understand what is happening. A statement with this level of business logic will cause you to stumble for a few seconds at while you work out what you are testing. Where as code like this, will allow you to continue scanning.
if(CheckIfBusinessRuleIsTrue())
{
//Do Something
}
function CheckIfBusinessRuleIsTrue()
{
return (the best solution from previous posts here);
}
Name your function explicitly so it immediately obvious what you are testing and your code will be much easier to scan and understand.
You could put all the answers into a Javascript Set and then just call .contains() on the set.
You still have to declare all the contents, but the inline call will be shorter.
Something like:
var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}
One of my favorite ways of accomplishing this is with a library such as underscore.js...
var isItem = _.some(['itema','itemb','itemc','itemd'], function(item) {
return test.type === item;
});
if(isItem) {
// One of them was true
}
http://underscorejs.org/#some
another way or another awesome way i found is this...
if ('a' in oc(['a','b','c'])) { //dosomething }
function oc(a)
{
var o = {};
for(var i=0;i<a.length;i++) o[a[i]]='';
return o;
}
of course as you can see this takes things one step further and make them easy follow logic.
http://snook.ca/archives/javascript/testing_for_a_v
using operators such as ~ && || ((),()) ~~ is fine only if your code breaks later on. You won't know where to start. So readability is BIG.
if you must you could make it shorter.
('a' in oc(['a','b','c'])) && statement;
('a' in oc(['a','b','c'])) && (statements,statements);
('a' in oc(['a','b','c']))?statement:elseStatement;
('a' in oc(['a','b','c']))?(statements,statements):(elseStatements,elseStatements);
and if you want to do inverse
('a' in oc(['a','b','c'])) || statement;
Just use a switch statement instead of if statement:
switch (test.type) {
case "itema":case "itemb":case "itemc":case "itemd":
// do your process
case "other cases":...:
// do other processes
default:
// do processes when test.type does not meet your predictions.
}
Switch also works faster than comparing lots of conditionals within an if
For very long lists of strings, this idea would save a few characters (not saying I'd recommend it in real life, but it should work).
Choose a character that you know won't occur in your test.type, use it as a delimiter, stick them all into one long string and search that:
if ("/itema/itemb/itemc/itemd/".indexOf("/"+test.type+"/")>=0) {
// doSomething
}
If your strings happen to be further constrained, you could even omit the delimiters...
if ("itemaitembitemcitemd".indexOf(test.type)>=0) {
// doSomething
}
...but you'd have to be careful of false positives in that case (e.g. "embite" would match in that version)
For readability create a function for the test (yes, a one line function):
function isTypeDefined(test) {
return test.type == 'itema' ||
test.type == 'itemb' ||
test.type == 'itemc' ||
test.type == 'itemd';
}
then call it:
…
if (isTypeDefined(test)) {
…
}
...
I think there are 2 objectives when writing this kind of if condition.
brevity
readability
As such sometimes #1 might be the fastest, but I'll take #2 for easy maintenance later on. Depending on the scenario I will often opt for a variation of Walter's answer.
To start I have a globally available function as part of my existing library.
function isDefined(obj){
return (typeof(obj) != 'undefined');
}
and then when I actually want to run an if condition similar to yours I'd create an object with a list of the valid values:
var validOptions = {
"itema":1,
"itemb":1,
"itemc":1,
"itemd":1
};
if(isDefined(validOptions[test.type])){
//do something...
}
It isn't as quick as a switch/case statement and a bit more verbose than some of the other examples but I often get re-use of the object elsewhere in the code which can be quite handy.
Piggybacking on one of the jsperf samples made above I added this test and a variation to compare speeds. http://jsperf.com/if-statements-test-techsin/6 The most interesting thing I noted is that certain test combos in Firefox are much quicker than even Chrome.
This can be solved with a simple for loop:
test = {};
test.type = 'itema';
for(var i=['itema','itemb','itemc']; i[0]==test.type && [
(function() {
// do something
console.log('matched!');
})()
]; i.shift());
We use the first section of the for loop to initialize the arguments you wish to match, the second section to stop the for loop from running, and the third section to cause the loop to eventually exit.

Categories

Resources