Check exactly one boolean option set

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.

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");
 }

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.

@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.