marzzy
marzzy

Reputation: 788

How to use pattern matching without switch case in javascript (in functional programming way)

I've been reading the clean code book which ask for eliminate using switch case, So I wanted to implement it in JavaScript, and I wanted to use it in functional programming way, so I decided to use pattern matching.

I have the code below, that I want to omit the switch case, how can I do it, not using the polymorphism.

function calculateSalary(role) {
    switch (true) {
      case /CTO/.test(role):
        return getCTOSallary();
      case /CEO/.test(role):
        return getCEOSallary();
      case /engineer/.test(role):
        return getEngineeringSallary();
      default:
        return getGeneralSallary();
    }
}

Upvotes: 1

Views: 731

Answers (3)

customcommander
customcommander

Reputation: 18921

Disclaimer: I am the author of the library mentioned in this answer.


I like the way it is done in Clojure with multimethods:

A Clojure multimethod is a combination of a dispatching function, and one or more methods.

  1. A dispatching function takes parameter(s) and return a value
  2. The dispatched value is used to determine which method to call

The library @customcommander/multifun is an attempt at bringing multimethods to JavaScript.

You first need a dispatching function

This will be getRole. It returns the part you're interested in or null if the role is unknown:

const getRole = role => {
  const match = role.match(/(cto|ceo|engineer)/i);
  return match ? match[1].toLowerCase() : null;
};

Then you'll need a series of value/function pairs

  • 'cto', getCTOSalary
  • 'ceo', getCEOSalary
  • ...

Finally you need a function when there are no matches

This will be the getGeneralSalary function.

How does it work?

  • getRole is the dispatching function, it is applied to the parameters and returns a value
  • The dispatched value is compared with the value in each value/function pair.
  • If there's a match, the function is applied to the parameters
  • The function getGeneralSalary is applied to the parameters when there are no matches.

const multifun = require("@customcommander/multifun");

const getCTOSalary = (_, {bonus = 0}) => 10 + bonus;
const getCEOSalary = (_, {bonus = 0}) => 20 + bonus;
const getEngineeringSalary = (_, {bonus = 0}) => 30 + bonus;
const getGeneralSalary = (_, {bonus = 0}) => 40 + bonus;

const getRole = role => {
    const match = role.match(/(cto|ceo|engineer)/i);
    return match ? match[1].toLowerCase() : null;
};

const calculateSalary =
    multifun
        (   getRole
        ,   'cto', getCTOSalary
        ,   'ceo', getCEOSalary
        ,   'engineer', getEngineeringSalary
        ,   getGeneralSalary
        );

calculateSalary('Chief Technology Officer (CTO)', {bonus: 1}); //=> 11
calculateSalary('Chief Executive Officer (CEO)', {bonus: 2});  //=> 22
calculateSalary('Senior Software Engineer', {bonus: 3});       //=> 33
calculateSalary('random title', {bonus: 4});                   //=> 44

Upvotes: 1

Aadit M Shah
Aadit M Shah

Reputation: 74234

One way to do so would be to use dynamic dispatch.

const Engineer = (name, bonus) => ({ constructor: Engineer, name, bonus });

Engineer.calculateSalary = engineer => 20 + engineer.bonus;

const Employee = (name, bonus) => ({ constructor: Employee, name, bonus });

Employee.calculateSalary = employee => 10 + employee.bonus;

const calculateSalary = role => role.constructor.calculateSalary(role);

console.log("John's salary is", calculateSalary(Engineer("John", 10))); // 30
console.log("Mary's salary is", calculateSalary(Employee("Mary", 10))); // 20

Note that we could have used classes for dynamic dispatch too. However, I wanted to show that it's possible to have dynamic dispatch without using classes, using a purely functional style of programming.

Upvotes: 2

Ilijanovic
Ilijanovic

Reputation: 14904

Do you mean something like this?

function test(str) {
   let tests = [/xyz/, /test/, /ing/];
   if(tests.some(patt => patt.test(str))) {
      console.log("Match found");
   }else {
      console.log("No match found");
   }
}

test("testing");

Upvotes: 0

Related Questions