Given there's no easy way to properly do this until TC39 implemented switch
pattern matching comes along, the best bet is libraries for now.
loadash
Go ol' loadash has a nice _.cond
function:
var func = _.cond([
[_.matches({ 'a': 1 }), _.constant('matches A')],
[_.conforms({ 'b': _.isNumber }), _.constant('matches B')],
[_.stubTrue, _.constant('no match')]
]);
func({ 'a': 1, 'b': 2 });
// => 'matches A'
func({ 'a': 0, 'b': 1 });
// => 'matches B'
func({ 'a': '1', 'b': '2' });
// => 'no match'
patcom
One of the recommended libraries to look at, which has feature parity with the TC39 proposal for switch
pattern matching, patcom, is quite small and nicely written - this is the main index.js
:
import { oneOf } from './matchers/index.js'
export * from './matchers/index.js'
export * from './mappers.js'
export const match =
(value) =>
(...matchers) => {
const result = oneOf(...matchers)(value)
return result.value
}
Here's the simple example:
import {match, when, otherwise, defined} from 'patcom'
function greet(person) {
return match (person) (
when (
{ role: 'student' },
() => 'Hello fellow student.'
),
when (
{ role: 'teacher', surname: defined },
({ surname }) => `Good morning ${surname} sensei.`
),
otherwise (
() => 'STRANGER DANGER'
)
)
}
So for yours something like this should work:
match (msg) (
when ({ operation: 'create' }), ({ block: b }) => {
model.blocks.push(b);
drawBlock(b);
}),
when ({ operation: 'select', properties: { snap: 'arbitrary' } }), ({ properties: { x: sx, y: sy }}) =>
doStuff(sx, sy)
)
when ({ operation: 'unselect', properties: { snap: 'specific' } }, ({ geometry: geom }) =>
doOtherStuff(geom)
)
)
match-iz
For people wanting to implement the whole thing themselves there is a recommended small library match-iz that implements functional pattern matching in currently 194 lines.
Supercharged switch
I'm wondering if something like this 'supercharged switch' might get close to what your after:
const match = (msg) => {
const { operation, properties: { snap } } = msg;
switch (true) {
case operation === 'create':
model.blocks.push(b);
drawBlock(b);
break;
case operation === 'select' && snap === 'arbitrary':
const { properties: { x: sx, y: sy }} = msg;
doStuff(sx, sy);
break;
case operation === 'unselect' && snap === 'specific':
const { geometry: geom } = msg;
doOtherStuff(geom)
break;
}
}
Reducers
Also the whole concept of matching on strings within objects and then running a function based on that sounds a lot like Redux reducers.
From an earlier answer of mine about reducers:
const operationReducer = function(state, action) {
const { operation, ...rest } = action
switch (operation) {
case 'create':
const { block: b } = rest
return createFunc(state, b);
case 'select':
case 'unselect':
return snapReducer(state, rest);
default:
return state;
}
};
const snapReducer = function(state, action) {
const { properties: { snap } } = action
switch (snap) {
case 'arbitrary':
const { properties: { x: sx, y: sy } } = rest
return doStuff(state, sx, sy);
case 'specific':
const { geometry: geom } = rest
return doOtherStuff(state, geom);
default:
return state;
}
};