Reputation: 2727
How do you pass a switch statement as a function argument in Javascript?
I want to do this with Redux library:
const agendaPointsReducer = function (state = initialState, action) {
return Object.assign({}, state, switch (action.type) {
case OPEN_AGENDA_POINT: {
state.modal_is_open = true,
state.point_id = action.id
}
case CLOSE_AGENDA_POINT: {
state.modal_is_open = false
}
})
}
But I get an unexpected token error. I know this could be possible in Coffeescript, is there a way to do this in Javascript es6?
Upvotes: 0
Views: 3738
Reputation: 23397
I think the answers given by daniel_franz and jfriend00 are the best and most straightforward solutions. But it's still fun to think about alternatives :)
I had some fun experimenting with trying to store switch
logic in a Map
. It probably has many downsides (for one, you can't chain cases by omitting a break
), and it makes stuff overly complicated, so I guess we'll have to look at it as just an exploration and not as a "this is how I would do it in a real project"...
The approach:
Map
with for each case
, an entrycase
as a keyMap
's getItem
returns null
, which we'll wrap in a function as wellThe code:
const apply = f => typeof f === "function" ? f() : null;
const Switch = (input, ...entries) => apply(new Map(entries).get(input));
const types = {
INCREMENT: "INCREMENT",
DECREMENT: "DECREMENT",
SQUARE: "SQUARE"
};
const myNumberReducer = function(state = { myNumber: 0 }, action = {}) {
return Object.assign({}, state, Switch(action.type,
[ types.INCREMENT, () => ({ myNumber: state.myNumber + 1 }) ],
[ types.DECREMENT, () => ({ myNumber: state.myNumber - 1 }) ],
[ types.SQUARE, () => ({ myNumber: state.myNumber * state.myNumber }) ]
));
}
let lastState = myNumberReducer();
console.log(lastState); // myNumber: 0
lastState = myNumberReducer(lastState, { type: types.INCREMENT });
console.log(lastState); // myNumber: 1
lastState = myNumberReducer(lastState, { type: types.INCREMENT });
lastState = myNumberReducer(lastState, { type: types.SQUARE });
console.log(lastState); // myNumber: 4
Upvotes: 1
Reputation:
Additionally to the given answers specific to your question I want to provide you a more general answer.
To pass a switch
statement to a function you need to transform it into an expression. There are two distinct ways to achieve this.
switch
in a functionconst apply = f => x => f(x);
const enumerate = apply(x => {
switch (x.constructor) {
case Number: {
return inc(x);
}
case String: {
return succ(x);
}
default: throw TypeError();
}
});
const inc = x => x + 1;
const succ = x => String.fromCharCode(x.charCodeAt(0) + 1);
console.log("enum 1:", enumerate(1)); // 2
console.log("enum a:", enumerate("a")); // "b"
With this approach I use apply
as a little auxiliary function. It makes the calling code more readable, e.g. Object.assign({}, state, apply(x => {/* switch statement */}) (action.type))
switch
itself as a functionconst caseOf = (...pairs) => x => {
let g;
pairs.some(([guard, f], i) => guard(x)
? (g = f, true)
: false);
return g(x);
}
const inc = x => x + 1;
const succ = x => String.fromCharCode(x.charCodeAt(0) + 1);
const enumerate = caseOf(
[x => x.constructor === Number, x => x + 1],
[x => x.constructor === String, x => String.fromCharCode(x.charCodeAt(0) + 1)]
);
console.log("enum 1:", enumerate(1));
console.log("enum a:", enumerate("a"));
caseOf
is a normal function, a higher order function to be more precise. You can apply it wherever an expression is allowed.
Destructuring assignment is an non-obvious advantage that both approaches have over normal switch
statements. You can apply destructuring assignment without additional variables:
const apply = f => x => f(x);
const sqr = x => x * x
const xs = [2],
ys = [];
console.log(
sqr(apply(([x]) => { // <= destructuring assignment
switch (x) {
case undefined: return 0;
default: return x;
}
}) (xs))
);
console.log(
sqr(apply(([x]) => { // <= destructuring assignment
switch (x) {
case undefined: return 0;
default: return x;
}
}) (ys))
);
const caseOf = (...pairs) => x => {
let g;
pairs.some(([guard, f], i) => guard(x)
? (g = f, true)
: false);
return g(x);
}
const sqr = x => x * x;
const isAssigned = x => x !== undefined && x !== null;
const always = x => _ => x;
const xs = [5],
ys = [];
console.log(
caseOf(
[([x]) => isAssigned(x), sqr], // <= destructuring assignment
[always(true), always(0)] // default case
) (xs)
);
console.log(
caseOf(
[([x]) => isAssigned(x), sqr], // <= destructuring assignment
[always(true), always(0)] // default case
) (ys)
);
I hope that helps.
Upvotes: 1
Reputation: 111
I think what you might really want to do is something like this:
const agendaPointsReducer = function (state = initialState, action) {
switch (action.type) {
case OPEN_AGENDA_POINT: {
return {
...state,
modal_is_open: true,
point_id: action.id
};
}
case CLOSE_AGENDA_POINT: {
return {
...state,
modal_is_open: false,
};
}
}
}
Upvotes: 1
Reputation: 708036
So, Object.assign()
copies properties from the objects you pass it onto the destination object. So, what you need to pass it is objects with properties that it can copy from.
If what you're trying to do is to create an object that has a property based on the current execution of a switch statement (which is my guess for what you're trying to do), then I'd suggest you just create the source object before calling Object.assign()
and pass that object to it.
Doing it the way I show here will avoid any side effects that would change the passed in object too (that's why we set the properties in the switch statement on a local object, not on the state
object):
const agendaPointsReducer = function (state = initialState, action) {
let srcObj = {};
switch (action.type) {
case OPEN_AGENDA_POINT:
srcObj.modal_is_open = true;
srcObj.point_id = action.id;
break;
case CLOSE_AGENDA_POINT:
srcObj.modal_is_open = false;
break;
}
return Object.assign({}, state, srcObj);
}
Upvotes: 1
Reputation: 111
One possibility is to wrap it into a function like this:
const agendaPointsReducer = function (state = initialState, action) {
return Object.assign({}, state, (() => {
switch (action.type) {
case OPEN_AGENDA_POINT: {
state.modal_is_open = true,
state.point_id = action.id
}
case CLOSE_AGENDA_POINT: {
state.modal_is_open = false
}
}
})())
}
Upvotes: 3
Reputation: 10672
switch
statements are not expressions in Javascript. In Coffeescript, everything is an expression (a la Ruby). Due to this, there is not a way to do this. Further, in the example you have just shown, there is no reason to, as your switch
statement is just modifying the state
variable which is already being passed.
Upvotes: 2