munkee
munkee

Reputation: 769

Improvement to my horrible switch statement for regex matching

I'm trying to get away from using horrible switch cases in node.js. I am looking for a more efficient way of testing an input against various regex cases. Dependent on the case that is matched I either fire an event or I do some transformation of the input before running another function.

To save having a really long block of code I have cut down my function to the skeleton below so it shows a focus on the switch.

I've taken a look at the possibility of using .map to return a true false but I'm unsure how best to implement that also.

Any advise or suggestions on the best way to do this?

function ParseLogMessages(message, config, callback){
var _this = this;

try {
//Define regex in order to match strings based on case
_this.to_group = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}");
_this.from_group=new RegExp("^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}");
_this.to_person = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s[a-zA-Z0-9 \\- _]+\\s\\<{3}.+");
_this.from_person = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s\\w+\\s\\>{3}");
_this.contact = new RegExp("(User #+\\d+:)");


_this.contact = new RegExp("(User #+\\d+:)");

//Test message against each to find type
switch (true){
    //Message sent to a group chat
    case _this.to_group.test(_this.payload.raw):
    break;

    //Message from a group chat
    case _this.from_group.test(_this.payload.raw):
    break;

    //Message sent to a person from the bot
    case _this.to_person.test(_this.payload.raw):
    break;

    //Message sent from a person to the bot
    case _this.from_person.test(_this.payload.raw):         
    break;

    //Contact shared
    case _this.contact.test(_this.payload.raw):     
    break;

    default:
    break;


}

callback(null,"Logfile message parsed ok!");

  } catch(err) {
    log.error(err);
    return callback(err,null);
  }

}

Upvotes: 2

Views: 159

Answers (3)

Ted Hopp
Ted Hopp

Reputation: 234847

You can create an array of regex/function pairs and loop through the array:

_this.tests = [
    { regex: new RegExp("^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}"), // to_group
      action: ... // action for to_group
    },
    { regex : new RegExp("^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}"), // from_group
      action: ... // action for from_group
    },
    // etc.
];

Then you can loop through the array, testing, and breaking when the test works:

for (i=0; i<tests.length; ++i) {
    if (tests[i].regex.test(_this.payload.raw) {
        tests[i].action();
        break;
    }
}

Upvotes: 5

Nathan Tuggy
Nathan Tuggy

Reputation: 2244

What you want is to convert that into an associative array and match with a loop. Untested code that should work:

let patterns = {
    "^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}": funcToGroup /* code for this case, preferably a [reference to a] function object without the parens */,
    "^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}": function () {
        // An inline anonymous function is also fine
    },
    "^\\[\\d{2}:\\d{2}\\]\\s[a-zA-Z0-9 \\- _]+\\s\\<{3}.+": funcToPerson,
    "^\\[\\d{2}:\\d{2}\\]\\s\\w+\\s\\>{3}": funcFromPerson,
    "(User #+\\d+:)": funcContactShared
};

for (let pat in _this.patterns) {
    if (new RegExp(pat).test(_this.payload.raw)) {
        _this.patterns[pat]();  // Actually execute the relevant case
    }
}

That should handle all the code within the try block.

Upvotes: 0

Guffa
Guffa

Reputation: 700562

You can put the objects in an array and call the test function until one returns true:

var o = [
  _this.to_group,
  _this.from_group,
  _this.to_person,
  _this.from_person,
  _this.contact
];
for (var i in o) {
  if (o[i].test(_this.payload.raw)) {
    // got a match
    break;
  }
}

Upvotes: 2

Related Questions