Let's say I have an Array consist of Boolean.
myArray = [false,false,false]
And I will need to do different stuff according to every status.
ex :
[false,true,false], [false,false,true], [true,true,false], ...
and so on.
Usually this kind of operation is done by checking each individual element to see fit.
ex:
if(!myArray[0] && myArray[1] && !myArray[2]) do A
if(!myArray[0] && !myArray[1] && myArray[2]) do B
if(myArray[0] && myArray[1] && !myArray[2]) do C
...
Instead of doing that. I convert the array into String:
switch(JSON.stringify(myArray))
case : "[false,true,false]"
do A
break
case : "[false,false,true]"
do B
break
case : "[true,true,false]"
do C
break
......
Is this considered good practice? Is there any hidden error I might face with this method?
Will this method increase readability ?
About Using JSON.stringify
The use of JSON.stringify will work fine for arrays (of arrays (of arrays...)) of (JSON-compatible) primitives, i.e. boolean, string, number, null. There is no ambiguity. The ECMAScript specification also is clear about the "gap" to produce between distinct values: it should be the empty string, unless explicitly defined differently via the optional argument. See ECMAScript Language Specification on the topic.
One caveat is that undefined is not a thing in JSON, and so [null] and [undefined] would both be stringified to "[null]". But since you are only using false and true, this is not an issue.
Now to your questions:
Is this considered good practice?
That is a matter of opinion. My first intuition says "no", mainly because of the type conversion, but since there are no caveats for your particular use case (i.e. array of boolean), we cannot really have much to criticise about.
Is there any hidden error I might face with this method?
No
Will this method increase readability ?
Yes
There is however at least one alternative that has all the same advantages, but does not rely on some data type conversion:
Alternative: bitset
In case your array is an array of boolean, you can opt to use a numeric data type instead, where each binary bit of a number corresponds to a boolean in the array version.
It is to be expected that this method will be more efficient than the strinfigication option, because that data type conversion obviously takes some time.
Here is a run of the original array pattern:
let arr = [false, false, true];
if (!arr[0] && arr[1] && !arr[2]) console.log("do A");
if (!arr[0] && !arr[1] && arr[2]) console.log("do B");
if (arr[0] && arr[1] && !arr[2]) console.log("do C");
Here is the same logic implemented with a bitset:
let bitset = 0b001;
if (bitset === 0b010) console.log("do A");
if (bitset === 0b001) console.log("do B");
if (bitset === 0b110) console.log("do C");
If ever you need more bits than are available in the number data type (53 bits), then you can go for BigInt. For instance:
let bitset = 0b1010101010101010101010101010101010101010101010101010101n;
// A partial comparison
if ((bitset & 0b100010001n) === 0b100010001n) console.log("do A");
Of course, it is not necessary to use 0b literals; this is just to highlight the correspondence with the array version. You can save some characters by writing numbers in hexadecimal notation (0x) or even plain decimal, although the latter would be more obscure for the reader of your code.
Not much wrong with it per se, depends on the context; how many of these booleans are there, where do they come from / what sets them, how many cases, etc. Bad part could be that it can be very hard to understand what is happening and to debug, especially if you have 10+ values in there.
You could perhaps convert each boolean to a string/word that means something in that context, so you get a sentence. If you have a unique word for each boolean, you could leave out the false ones so you only have to write down certain combinations.
Also instead of JSON.stringify() you can use myArray.join(',') which outputs true,true,false (so without the brackets).
Your goal is to write a readable if?
Stringify is a solution, but (maybe only for me) it is a little bit strange.
Here is my advice. Write a function like this:
isEqual(left, right) {
if (left.length !== right.length) return false;
for (var i = 0; i < left.length; i++) {
if (left[i] !== right[i]) return false;
}
return true;
}
And use it like this:
if (isEqual(array, [true, false, true])) alert('isEqual');
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript: The prettiest way to compare one value against multiple values
Javascript If statement used to check file extensions not working
In JS I'm trying to check whether an extension ends in "png" "jpg" or "gif". I'm aware this can be done with a switch statement, but I'm wondering if there's a simpler way to throw it all in the if conditional. Like:
if (aExtensions[i].toLowerCase() == ('jpg' || 'png' || 'gif')) {}
What's the best way to achieve this?
You could use an array like this:
var extensions = ["jpg", "png", "gif"];
if (extensions.indexOf(aExtensions[i].toLowerCase()) > -1) {
// match
}
In this case, you store the "valid" extensions. Then, you can use the indexOf method of Array to find if any of the items in the array match the specific extension you're looking at - checking for a value that is 0 or greater.
indexOf isn't supported on older browsers, so you'd need to include a polyfill to back it up. There are several solutions for that.
Of course, if you wanted to only use if statements, you could use this format:
var ext = aExtensions[i].toLowerCase();
if (ext == "jpg" || ext == "png" || ext == "gif") {
// match
}
Another possibility I can think of is a switch statement, like:
var ext = aExtensions[i].toLowerCase();
switch (ext) {
case "jpg":
case "png":
case "gif":
// match
break;
case default:
// No match
}
I might as well include it (other answers had it first, definitely), but one more I can think of is using a regex, like:
if (/jpg|png|gif/i.test(aExtensions[i])) {
// match
}
But note that you will never be able to individually get the extensions back, and that's why I would prefer one of the first two options I gave.
What about a regex?
if ( /jpg|png|gif/i.test( aExtensions[i] ) ) {
...
}
Yet another way to do this is by using an object literal like this,
if ({'jpg':1,'png':1,'gif':1}[aExtensions[i].toLowerCase()]) {
// ...
}
This way you're looking to see if the anonymous object {'jpg':1,'png':1,'gif':1} has a property which will be true by the if. 1 meets this, undefined doesn't.
If you're using an object, you could also make use of the in operator (as pointed out by Ian) which gives true if the object has such a key irrelevant of it's value.
if (aExtensions[i].toLowerCase() in {'jpg':0,'png':1,'gif':2}) {
// ...
}
Note this time how 'jpg' still passes even though 'jpg':0.
You could use a variable and triple comparison, but that's ugly.
Or you could just use an array and check if the string is contained in it.
if (~ ["jpg", "png", "gif"].indexOf(aExtensions[i].toLowerCase()) …
Yet, for the complicated != -1 comparison and the lack of native indexOf in older IEs, people tend to use regexes:
if (/jpg|png|gif/i.test(aExtensions[i])) …
This question already has answers here:
Check variable equality against a list of values
(16 answers)
Closed 5 years ago.
I feel like I come across this a lot and that intuitively, there should be a way to do something like this:
if (userType ==="admin" || userType === "superUser"){
// do stuff
}
In a more elegant way, like this:
if (userType === ("admin" || "superUser")){
// do stuff
}
Obviously that^ doesn't work because if the first value resolves to true, it will never check if it's the second ("superuser").
Is there shorthand to do this in a JS if-statement where you wouldn't have to repeat the variable name?
Switch statements don't count! ;D
JavaScript doesn't provide out of the box such a syntax.
Now, you can do something of close enough with the Array.includes() method.
It returns true if the element is found in the array.
Otherwise if returns false.
var userTypes = ["admin", "superUser"];
if (userTypes.includes(userType)){
// do stuff
}
or by inlining the array value :
if (["admin", "superUser"].includes(userType)){
// do stuff
}
you can use an array with indexOf. something like
if(["superUser", "admin"].indexOf(userType) >= 0){
//code goes here
}
You could use an object for fast checking.
if ({ admin: 1, superUser: 1 }[userType]) {
// do something if true
}
This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 6 years ago.
Is it possible to shorten this code?
var access_followup = user_access && user_access.followup && user_access.followup.access ? true : false;
Unfortunately JS does not have a null conditional operator. You could write helper function for it or use a slightly less effective method of creating dummy objects:
var access_followup = !!((user_access || {}).followup || {}).access;
which is shorter and prevents using the property names more than once, but doesn't improve readability. The !! is used to enforce a boolean value even when the values don't exist
Maybe I am answering the wrong thing, but why would you want to make it shorter? I'd vote to make it a bit longer, but easier to read for people who work with your code ( including you :) ).
You could make it more readable by splitting it up into multiple lines:
var access_followup = (
user_access &&
user_access.followup &&
user_access.followup.access === true // if access is a boolean value
);
Or, in case you really really want to have short code and you do not use a minifier already, you can try https://jscompress.com/ (which actually compresses any code you paste into it! but makes it WAY less readable).
If the first 2 checks are because you are protecting against exception thrown when user_access.followup is undefined, you can try this:
var accessFollowup;
try {
accessFollowup = !!user_access.followup.access;
} catch (e) {
accessFollowup = false;
}
You could also shorten by removing just the ternary by using !! to force last element into Boolean value:
var access_followup = !!user_access && !!user_access.followup && !!user_access.followup.access
very ugly code that works
var access_followup = (followup = (user_access || {}).followup) && followup.access;
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.