Yatrix
Yatrix

Reputation: 13795

Is there a clever way/trick to reuse a switch statement that does different things in the cases?

I use the same switch statement over and over, but the functionality in each case my differ.

switch(type){
  case "t1": 
    fnA();
    break;  
  case "t2": 
    fnB();
    break;
  ...
}

switch(type){
  case "t1": 
    fnZ();
    break;  
  case "t2": 
    fnY();
    break;
  ...
}

I don't know if there's a better or more clever way to do this. I couldn't think of anything, so I thought I'd toss it to you fine folks. Thanks in advance.

EDIT: to make more sense of it, think of it like this: in the first switch, depending on the type, data will be inserted added to an object a certain way. In the second switch, data will be deleted from an object a certain way.

Upvotes: 1

Views: 2019

Answers (5)

trincot
trincot

Reputation: 351029

You could store the functions grouped by possible values for type:

// Preparation
const typeFuncs = {
    t1: [fnA, fnZ],
    t2: [fnB, fnY]
};

// First switch can be replaced by
typeFuncs[type][0]();

// Second switch can be replaced by
typeFuncs[type][1]();

Now instead of using arrays of functions, you could think of more meaningful names of the task to perform:

// Preparation
const typeFuncs = {
    t1: { doFirst: fnA, doSecond: fnZ },
    t2: { doFirst: fnB, doSecond: fnY }
};

// First switch can be replaced by
typeFuncs[type].doFirst();

// Second switch can be replaced by
typeFuncs[type].doSecond();

And then you could create those objects with a constructor, and instead of using a type that is a string, assign to type the object itself:

const fnA = () => console.log("calling fnA");
const fnB = () => console.log("calling fnB");
const fnY = () => console.log("calling fnY");
const fnZ = () => console.log("calling fnZ");

// Preparation
class Type {
    constructor(name, f1, f2) {
        this.name = name;
        this.doFirst = f1;
        this.doSecond = f2;
    }
}

// Enumeration of types
const Types = {
    T1: new Type("t1", fnA, fnZ),
    T2: new Type("t2", fnB, fnY)
};

// The variable type is set to one of the types (instead of a string):
const type = Types.T1;

// First switch can be replaced by
type.doFirst();

// Second switch can be replaced by
type.doSecond();

Upvotes: -1

naugtur
naugtur

Reputation: 16915

This might be a shot in the dark, but I guess that repeating switch-case multiple times in your code is just a result of a bad architecture decision.

You have probably assigned responsibilities incorrectly.

Asuming you use modules not ObjectOriented stuff - the methods that are being called should be mixins. Or use a decorator pattern.

Upvotes: 1

Philipp
Philipp

Reputation: 69703

I think this looks like a good situation to use object-oriented programming.

  1. Create a class for each type
  2. Whenever you need to do something which is doing something differently depending on the type, call type.someFunction(). Make each type class implement this function differently.

Upvotes: 4

Evan Hahn
Evan Hahn

Reputation: 12737

I'd hesitate implement this solution, because it's not much more concise than a switch and it's more confusing.

function doSwitch(expression, options) {
    options[expression]();
}

doSwitch(type, {
    "t1": fnA,
    "t2": fnB
});

doSwitch(type, {
    "t1": fnZ,
    "t2": fnY
});

Don't do this because of performance -- do this because you have huge repeated switch statements. If you have such statements, you might want to re-evaluate that.

Upvotes: 0

Bhushan Firake
Bhushan Firake

Reputation: 9458

Usually a switch statement is not a processing bottleneck, so it's the last thing you optimize.

Each case translates onto the CPU as a test and a jump, so think of how many operations your worst case would be and decide whether it's worth fussing over, and how many operations you would actually save (both worst case and average).

Saying that, there's a couple of obvious options. If you have numeric types and they are evenly distributed, you can split your switch statement into multiple switches....

switch(type){
  case "t1": 
    fnA();
    break;  
  case "t2": 
    fnB();
    break;
  ...
}

switch(type){
  case "t1": 
    fnZ();
    break;  
  case "t2": 
    fnY();
    break;
  ...
}

This is of course trickier to maintain, and may rely upon knowledge of constant values that makes your code ugly. It's really just a grungey search tree...

Another way is to put all your cases into functions, and build a tree or hash table that maps each value to a function handler. That way you can always rely on O(logN) search times (or better, in the case of hash tables). I repeat: don't do this unless you have a very good reason to.

Hope that helps give you some insight.

Upvotes: 0

Related Questions