Monday, November 3, 2014

Using objects for switch statements

I ran across a post where the author wished switch statements returned a value. The switch statement has always looked to me like a poor choice in programming. It seems like we should be able to use polymorphism instead of what is another version of a long if/else chain. However, there are cases when we could be over-designing and a switch statement would do the job.
// Not valid ECMAScript
var color = switch(x) {
  case   1: 
    'blue' ; break;
  case   2: 
    'red'  ; break;
  default : 
    'green';
};

The author, Dusan Jovanovich, proposed a cond function in the likes of lisp
cond( x, 
      1, 'blue', 
      2, 'red', 
      'green' );
Since lisp is the language where non-primitives are lists, and JavaScript is the language where non-primitives are basically maps, I would suggest a cond function that feels more like JavaScript.
function cond(val, cases, fallback, scope) {
    var theCase = cases.hasOwnProperty(val) ? cases[val] : fallback;
    return typeof theCase == "function" ? 
        theCase.call(scope || this, val) : 
        theCase; 
}
So that the previous cond call would look like:
var color = cond(x , {
   1: 'blue',
   2: 'red',
}, 'green');
One of the benefits of this function is that you can pass functions to each condition, so that we get almost all functionality of a switch:
jQuery('a').each(function(){
 switch(x) {
  case 1: 
    $(this).css({ color: 'blue'});
    $this.slideDown();
    break ;
  case 2: 
    $(this).css({ color: 'red'});
    $this.slideUp();
    break ;
  default : 
    $(this).css({ color: 'green'});
 }
});

// Could become
jQuery('a').each(function(){
  cond(x, {
    1: function() {
        $(this).css({ color:'blue'});
        $(this).slideDown();
    },
    2: function() {
        $(this).css({ color:'red'});
        $(this).slideUp();
    },
  }, function() {
        $(this).css({ color:'green'});
  }, this);
});
If you have multiple cases where the almost the same thing needs to happen (fallthroughs), the switched value is passed to each of the conditions, so you could nest them. At this point, this probably would not be a good idea, but here it goes anyway.
jQuery('a').each(function(){
  // Save 'this' instead of passing in a scope in this case
  var $this = $(this);
  
  function fallThrough12(y) {       
      cond(y, {
           1: function() {
              $this.css({ color:'blue'});
           }
           2: function() {
              $this.css({ color:'red'});
           }
      });
      $this.slideDown();
  }
  cond(x, {
    1: fallThrough12,
    2: fallThrough12,
    3: function() {
        $this.css({ color:'yellow'});
        $this.fadeOut();
    }
  }, function() {
        $this.css({ color:'green'});
  });