ocomfd
ocomfd

Reputation: 4020

How to eliminate if-else when doing different things according to different combinations of boolean value?

for example, suppose I need to do different things according to combinations of boolean values: cond_0,cond_1 and cond_2 :

cond_0 cond_1 cond_2
false  false  false  a();
false  false  true   b();
.
.
.
true   true   true   h();

it looks as if mapping bit numbers to functions:

000:a()
001:b()
.
.
.
111:h()

while the general rule looks like very simple, I don't know how to write it without if-else, and the current form looks like that:

var f=function(cond_0,cond_1,cond_2){
  if(!cond_0 && !cond_1 && !cond_2){
    a();
  }else if( cond_0 && !cond_1 && !cond_2)){
    b();
  }else if(!cond_0 &&  cond_1 && !cond_2)){
    c();
  }else if( cond_0 &&  cond_1 && !cond_2)){
    d();
  }else if(!cond_0 && !cond_1 &&  cond_2)){
    e();
  }else if( cond_0 && !cond_1 &&  cond_2)){
    f();
  }else if(!cond_0 &&  cond_1 &&  cond_2)){
  g();
  }else if( cond_0 &&  cond_1 &&  cond_2)){
    h();
  }
}

which is very long and hard to read. And when a new boolean condition cond_3 is added, it is horrible to modify the code:

if(!cond_0 && !cond_1 && !cond_2 && !cond_3){
    a();
  }else if( cond_0 && !cond_1 && !cond_2 !cond_3)){
    b();
  }
  .
  .
  .

Is there any way to eliminate the if else, so that cond_0 , cond_1 and cond_2 can just appear once only inside the function, and also easy to add new function when cond_3 is added? I want something like:

var f=function(cond_0,cond_1,cond_2){
  var magic=(000:a,001:b,010:c...);
  magic(cond_0,cond_1,cond_2)();
}

Upvotes: 8

Views: 445

Answers (6)

Travis J
Travis J

Reputation: 82297

  • I need to do different things according to combinations of boolean values
  • and also easy to add new function when cond_3 is added

There is no need to name them, just iterate over the set of arguments in the function.

//range of preset functions styled as OP described
function a(){ console.log('a'); }
function b(){ console.log('b'); }
function c(){ console.log('c'); }
function d(){ console.log('d'); }
function e(){ console.log('e'); }
function f(){ console.log('f'); }
function g(){ console.log('g'); }
function h(){ console.log('h'); }
function i(){ console.log('i'); }
function j(){ console.log('j'); }
function k(){ console.log('k'); }
function l(){ console.log('l'); }
function m(){ console.log('m'); }

//This function will take the arguments, reduce their boolean value 
//to a 1 or 0 using |0, concatenate the string of 1's and 0's,
//parse out the string from binary to decimal, and then
//access the array's index correlating to the given decimal.
var truthTable = function(){
    var callers = [a,b,c,d,e,f,g,h,i,j,k,l,m];
    var tar = [].reduce.call(arguments,(p,c) => p+(c|0),"");
    callers[parseInt(tar,2)]();
};
truthTable (true,true,false);
truthTable (true,false,true);
truthTable (true,true);
truthTable (true,true,false,false);

As written, this will scale as far as it needs to provided that the decimal index of each function in callers correlates to its truth table value.

Upvotes: 0

Luke Kot-Zaniewski
Luke Kot-Zaniewski

Reputation: 1161

You had the right idea at the top. You can encode the functions in a map just like you put together conceptually by implicitly casting booleans to integers. Below is an implementation which can be easily extended to handle more states/functions.

//initialization
var bitmap = {};
bitmap[0] = a;
bitmap[1] = b;
bitmap[10] = c;
bitmap[11] = d;
bitmap [110] = e;

var inputBools = [true, true, false];

pickFunction(inputBools);


function pickFunction(bools){
  var key = 0;
  for (var i = 0; i < bools.length; i++){
    key += bools[bools.length-1-i]*Math.pow(10,i);
  }
  console.log(bitmap[key].call());
}

function a(){
  return "a";
}

function b(){
  return "b";
}

function c(){
  return "c";
}


function d(){
  return "d";
}

function e (){
   return "e";
 }



Upvotes: 0

Bergi
Bergi

Reputation: 665130

Use a bit of bitwise operations and binary literals as object keys:

function f(cond_0, cond_1, cond_2) {
  const magic = {
    0b000: a,
    0b001: b,
    0b010: c,
    0b011: d,
    0b100: e
    0b101: f,
    0b110: g,
    0b111: h,
  };
  return magic[cond_2 << 2 | cond_1 << 1 | cond_0 << 0]();
}

This object is essentially the same as the array literal in @Slai's answer, just with explicit indices.

It gets much crazier if you directly declare your functions as methods of the object instead of naming them individually:

const magic = {
  0b000() { console.log("a"); … },
  0b001() { console.log("b"); … },
  0b010() { console.log("c"); … },
  0b011() { console.log("d"); … },
  0b100() { console.log("e"); … },
  0b101() { console.log("f"); … },
  0b110() { console.log("g"); … },
  0b111() { console.log("h"); … },
};

Upvotes: 1

Vivek
Vivek

Reputation: 2755

See this implementation. Here all the three parameters (boolean expected) are multiplied by their place values to form the number and then padded with '0' to the desired length. This value is searched for in the magic object and the function if defined is called.

var a = function() {
  console.log('a() called');
};
var b = function() {
  console.log('b() called');
};
var c = function() {
  console.log('c() called');
};

var f = function(cond_0, cond_1, cond_2) {
  var cond = ((cond_0 * 100) + (cond_1 * 10) + (cond_2)).toString().padStart(3, '0');
  var magic = {
    '000': a,
    '001': b,
    '010': c
  };
  if (typeof magic[cond] === 'function') {
    magic[cond]();
  } else {
    console.log('No function defined for this cond');
  }
}

f(false, false, false);
f(false, false, true);
f(false, true, false);
f(true, false, false);

Upvotes: 4

Slai
Slai

Reputation: 22876

Because true to number is 1, true * 2 is 2, etc.. probably something like this:

function f(c0, c1, c2) { [a, b, c, d, e, f, g, h][+c0 +c1 * 2 +c2 * 4](); }    // magic

Upvotes: 2

Geuis
Geuis

Reputation: 42277

Look at switches on mdn: switch https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch

Upvotes: -1

Related Questions