Check exactly one boolean option set - javascript

Well, this is kind of hacky:
function b2n(boo) {
return boo ? 1 : 0;
}
if(b2n(opt1) + b2n(opt2) + b2n(opt3) !== 1) {
throw new Error("Exactly one option must be set");
}
Is there a better way to do this in Javascript? Using any of
more intelligent boolean/number handling
sneaky array or functional operations
And so forth. Javascript and Node solutions welcome.
In my actual problem, the options are coming from the Node module commander, so I'm not dealing with true boolean, just truthy and falsy things. There may be a commander-solution too.

Assuming you had an array of options, you could do:
if(opts.filter(Boolean).length !== 1) {}
It seems to me though that you ought to have one variable with three possible states instead...
var opt = 'a'; // (or 'b', or 'c')

You can do this :
if ( !!opt1 + !!opt2 + !!opt3 !== 1 ) {
It works because
!! makes a boolean from any value (true if the objects evaluates as true in if(value))
when adding booleans you get 1 for true and 0 for false.

You mentioned in your comment that this is coming from a commander options object.
You can do this more elegantly using Lodash:
if (_(options).values().compact().size() === 1)
If you only want to count a subset of the options, you can insert
.pick('a', 'b', 'c')

if ([opt1, opt2, opt3].reduce(function(x, y) { return x + !!y }, 0) == 1) {
// exactly one
};
ECMAScript 5 reduce function.

I think you are being too clever, what's wrong with:
var optionsSelected = 0;
if( opt1 ) optionsSelected++;
if( opt2 ) optionsSelected++;
if( opt3 ) optionsSelected++;
if( optionsSelected !== 1 ) {
throw new Error("Exactly one option must be set");
}
Of course I can play the clever game too:
if( opts.filter(Boolean).length !== 1 ) {
throw new Error("Exactly one option must be set");
}

#spudly is on the right track, but it could be a little more compact:
if( [opt1,opt2,opt3].filter(function(x){return x}).length!==1 ) {
throw new Error("Exactly one option must be set");
}
See ES5's filter method for more information.

Related

Faster and shorter way to check if a cookie exists

What is the shorter and faster way to know if a cookie has a value or exists?
I'm using this to know if exists:
document.cookie.indexOf('COOKIENAME=')== -1
This to know if has a value
document.cookie.indexOf('COOKIENAME=VALUE')== -1
Any better? Any problems on this method?
I would suggest writing a little helper function to avoid what zzzzBov mentioned in the comment
The way you use indexOf, it would only evaluate correct if you check for the containment of a String in a cookie, it doesn't match a complete name, in that case the above would return false therefore giving you the wrong result.
function getCookie (name,value) {
if(document.cookie.indexOf(name) == 0) //Match without a ';' if its the firs
return -1<document.cookie.indexOf(value?name+"="+value+";":name+"=")
else if(value && document.cookie.indexOf("; "+name+"="+value) + name.length + value.length + 3== document.cookie.length) //match without an ending ';' if its the last
return true
else { //match cookies in the middle with 2 ';' if you want to check for a value
return -1<document.cookie.indexOf("; "+(value?name+"="+value + ";":name+"="))
}
}
getCookie("utmz") //false
getCookie("__utmz" ) //true
However, this seems to be a bit slow, so giving it an other approach with splitting them
Those are two other possibilities
function getCookie2 (name,value) {
var found = false;
document.cookie.split(";").forEach(function(e) {
var cookie = e.split("=");
if(name == cookie[0].trim() && (!value || value == cookie[1].trim())) {
found = true;
}
})
return found;
}
This one, using the native forEach loop and splitting the cookie array
function getCookie3 (name,value) {
var found = false;
var cookies = document.cookie.split(";");
for (var i = 0,ilen = cookies.length;i<ilen;i++) {
var cookie = cookies[i].split("=");
if(name == cookie[0].trim() && (!value || value == cookie[1].trim())) {
return found=true;
}
}
return found;
};
And this, using an old for loop, which has the advantage of being able to early return the for loop if a cookie is found
Taking a look on JSPerf the last 2 aren't even that slow and only return true if theres really a cookie with the name or value, respectively
I hope you understand what i mean
Apparently:
document.cookie.indexOf("COOKIENAME=VALUE");
For me, is faster, but only slightly.
As the test shows, surprisingly, it's even faster to split the cookie up into arrays first:
document.cookie.split(";").indexOf("COOKIENAME=VALUE");
I use Jquery cookie plugin for this.
<script type="text/javascript" src="jquery.cookie.js"></script>
function isCookieExists(cookiename) {
return (typeof $.cookie(cookiename) !== "undefined");
}

about a replaceText function

In the following jQuery replaceText function, can someone please explain to me what the line remove.length && $(remove).remove(); does? I don't understand the usage of && here.
$.fn.replaceText = function( search, replace, text_only ) {
return this.each(function(){
var node = this.firstChild,
val,
new_val,
remove = [];
if ( node ) {
do {
if ( node.nodeType === 3 ) {
val = node.nodeValue;
new_val = val.replace( search, replace );
if ( new_val !== val ) {
if ( !text_only && /</.test( new_val ) ) {
$(node).before( new_val );
remove.push( node );
} else {
node.nodeValue = new_val;
}
}
}
} while ( node = node.nextSibling );
}
remove.length && $(remove).remove();
});
};
Due to the way conditions short circuit, $(remove).remove(); will only be evaluated if remove.length is true-ish.
Since remove is declared as an array, it's basically the same as:
if(remove.length != 0)
$(remove).remove();
The && here means that $(remove).remove() will only be executed if remove.length isn't 0.
This is because the && is a short circuit operator so it will not evaluate whatever comes after it if what is before it evaluates to false.
Though you got some right answers about what it does:
if(remove.length) // length is bigger than 0 (in this context)
$(remove).remove();
I must say that code is stupid, jQuery knows how to handle empty sets.
So when $(remove).length == 0 the code $(remove).remove(); just won't do anything.
You can simply use it without the if:
$(remove).remove();
it says :
if remove.length returns something other than 0 (which is equivalent to false) then remove the element
It is making sure that the "remove" object is set
if(remove.length != 0)
so that it does not try to remove something that does not exist. In jQuery, if the object $(remove) does not exist, the .length function will return 0 which coincides with False in javascript.
If it does exist (remove.length > 0) then it will be removed using the .remove() function. The naming is ambiguous for sure.
It's basically an in line condition statement to prevent an exception by calling .remove() on an object that doesn't exist.

is there a shorter way for this condition?

if (form.a.value !=""&&form.b.value!="" &&form.c.value !="")
is there a shorter way for this condition?
Javascript is weakly-typed so you can treat empty string as boolean false, so the following code should work:
if (form.a.value && form.b.value && form.c.value) {
However I don't know why would you want to change that code. Actually it's quite clear and verbose.
If you have only three fields(or less), you can leave it as is. If you have more(or unknown) number of fields to check, create an array of fields to check and do the checks in loop in separate function for better maintainability. Something like this:
if(!Empty([form.a,form.b,form.c]))
{
...
}
function Empty(elements)
{
for(var i=0;i<elements.length;i++)
{
if(elements[i].value)
return false;
}
}
there are lazy ways :)
if(form.a.value + form.b.value + form.c.value != "" )
if(form.a.value.length + form.b.value.length + form.c.value.length != 0 )
if(!form.a.value && !form.b.value && !form.c.value)

Javascript OR in an IF statement

I am trying to make an if statement in javascript that will do something if the variable does not equal one of a few different things. I have been trying many different variations of the OR operator, but I cant get it to work.
if(var != "One" || "Two" || "Three"){
// Do Something
}
Any ideas? Thanks!
Update:
I have tried this before:
if(var != "One" || var != "Two" || var != "Three"){
// Do Something
}
For some reason it does not work. My variable is pulling information from the DOM i dont know if that would effect this.
Actual Code
// Gets Value of the Field (Drop Down box)
var itemtype = document.forms[0].elements['itemtype' + i];
if(itemtype.value != "Silverware" || itemtype.value != "Gold Coins" || itemtype.value != "Silver Coins"){
// Do Something
}
Your expression is always true, you need:
if(!(myVar == "One" || myVar == "Two" || myVar == "Three")) {
// myVar is not One, Two or Three
}
Or:
if ((myVar != "One") && (myVar != "Two") && (myVar != "Three")) {
// myVar is not One, Two or Three
}
And, for shortness:
if (!/One|Two|Three/.test(myVar)) {
// myVar is not One, Two or Three
}
// Or:
if (!myVar.match("One|Two|Three")) {
// ...
}
More info:
De Morgan's Laws
Edit: If you go for the last approaches, since the code you posted seems to be part of a loop, I would recommend you to create the regular expression outside the loop, and use the RegExp.prototype.test method rather than String.prototype.match, also you might want to care about word boundaries, i.e. "noOne" will match "One" without them...
Assuming you mean "val does not equal One or Two or Three" then De Morgan's Theorem applies:
if ((val != "One") && (val != "Two") && (val != "Three")) {
// Do something...
}
For a shorter way to do it, try this format (copied from http://snook.ca/archives/javascript/testing_for_a_v):
if(name in {'bobby':'', 'sue':'','smith':''}) { ... }
or
function oc(a)
{
var o = {};
for(var i=0;i<a.length;i++)
{
o[a[i]]='';
}
return o;
}
if( name in oc(['bobby', 'sue','smith']) ) { ... }
The method mentioned by Mike will work fine for just 3 values, but if you want to extend it to n values, your if blocks will rapidly get ugly. Firefox 1.5+ and IE 8 have an Array.indexOf method you can use like so:
if(["One","Two","Test"].indexOf(myVar)!=-1)
{
//do stuff
}
To support this method on IE<=7, you could define a method called Array.hasElement() like so:
Array.prototype.hasElement = function hasElement(someElement)
{
for(var i=0;i<this.length;i++)
{
if(this[i]==someElement)
return true;
}
return false;
}
And then call it like so:
if(!["One","Two","Three"].hasElement(myVar))
{
//do stuff
}
Note: only tested in Firefox, where this works perfectly.
In addition to expanding the expression into three clauses, I think you'd better name your variable something other than var. In JavaScript, var is a keyword. Most browsers aren't going to alert you to this error.
Alternate way using an array:
var selected = ['Silverware', 'Gold Coins', 'Silver Coins'];
if ( selected.indexOf( el.value ) != -1 ) {
// do something if it *was* found in the array of strings.
}
Note: indexOf isnt a native method, grab the snippet here for IE:
https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/IndexOf

Is a JavaScript try-catch ignoring an expected occasional error bad practice?

In JavaScript is it wrong to use a try-catch block and ignore the error rather than test many attributes in the block for null?
try{
if(myInfo.person.name == newInfo.person.name
&& myInfo.person.address.street == newInfo.person.address.street
&& myInfo.person.address.zip == newInfo.person.address.zip) {
this.setAddress(newInfo);
}
} catch(e) {} // ignore missing args
If you expect a particular condition, your code will be easier to maintain if you explicitly test for it. I would write the above as something like
if( myInfo && newInfo
&& myInfo.person && newInfo.person
&& myInfo.person.address && newInfo.person.address
&& ( myInfo.person.name == newInfo.person.name
&& myInfo.person.address.street == newInfo.person.address.street
&& myInfo.person.address.zip == newInfo.person.address.zip
)
)
{
this.setAddress(newInfo);
}
This makes the effect much clearer - for instance, suppose newInfo is all filled out, but parts of myInfo are missing? Perhaps you actually want setAddress() to be called in that case? If so, you'll need to change that logic!
Yes. For one, an exception could be thrown for any number of reasons besides missing arguments. The catch-all would hide those cases which probably isn't desired.
I would think that if you're going to catch the exception then do something with it. Otherwise, let it bubble up so a higher level can handle it in some way (even if it's just the browser reporting the error to you).
On a related note, in IE, even though the specs say you can, you can not use a try/finally combination. In order for your "finally" to execute, you must have a catch block defined, even if it is empty.
//this will [NOT] do the reset in Internet Explorer
try{
doErrorProneAction();
} finally {
//clean up
this.reset();
}
//this [WILL] do the reset in Internet Explorer
try{
doErrorProneAction();
} catch(ex){
//do nothing
} finally {
//clean up
this.reset();
}
You could always write a helper function to do the checking for you:
function pathEquals(obj1, obj2, path)
{
var properties = path.split(".");
for (var i = 0, l = properties.length; i < l; i++)
{
var property = properties[i];
if (obj1 === null || typeof obj1[property] == "undefined" ||
obj2 === null || typeof obj2[property] == "undefined")
{
return false;
}
obj1 = obj1[property];
obj2 = obj2[property];
}
return (obj1 === obj2);
}
if (pathEquals(myInfo, newInfo, "person.name") &&
pathEquals(myInfo, newInfo, "person.address.street") &&
pathEquals(myInfo, newInfo, "person.address.zip"))
{
this.setAddress(newInfo);
}
For the example given I would say it was bad practice. There are instances however where it may be more efficient to simply trap for an expected error. Validating the format of a string before casting it as a GUID would be a good example.

Categories