JavaScript alternatives to handling switch cases that have properties - javascript

I have HTML elements defined like this
<div id="Jacob" validation="required alpha-numeric"></div>
<div id="Peter" validation="required minlen:4 maxlen:20"></div>
And in Javascript I was parsing and handling the validation="property1 property2 ... propertyN" like this:
// called from a foreach that uses split(' ')
validate(type) {
switch(type) {
case "required":
// ...
break;
case "alpha-numeric":
// ...
break;
}
}
I realise using a switch like this might be a bit verbose and archaic.
What would be an elegant way to parse parameters that have their own properties/values?

I don't know where you got the impression that switch statements are archaic. They're not.
As far as your particular case, you can use .split(":") to split the individual parts apart, and then match on that:
function validate(type) {
var parts = (type || "").split(":");
switch(parts[0].toLowerCase()) {
case "required":
// ...
break;
case "alpha-numeric":
// ...
break;
case "minlen":
// validate against parts[1]
break;
}
}
If you wanted to use a lookup rather than a switch, you can do that, but I'd say that's just a matter of preference:
var validators = {
"required": function (value) {
},
"alpha-numeric": function (value) {
},
"minlen": function (value, len) {
}
};
function validate (value, type) {
var parts = (type || "").split(":");
var validator = validators[parts[0].toLowerCase()];
if (validator) {
var result = validator(value, parts[1]);
}
}
One potential benefit of the second approach is that new validators can be added to the validator object at runtime, though if you wanted to make a pluggable system, you'd probably want to go with something more robust than just a plain object that can be arbitrarily modified.

you can create json of key function pair. And pick function using your type and call it.
var objOfFunction = {};
objOfFunction["required"] = requiredFunction;
objOfFunction["alpha-numeric"] = alphanFunction;
function validate(type) {
objOfFunction[type]();
}
function requiredFunction(){
alert("required");
}
function alphanFunction(){
alert("in alpha");
}
validate("required");
validate("alpha-numeric");

Related

Want to add new cases dynamically in switch statement using jquery [duplicate]

I need to dynamically add cases to a switch. I want the user to be able to add items and every item needs it's own switch case.
You can use object with callback functions instead:
// you can have initial casses
var callbacks = {
'something': [() => 42]
};
// and you can create new entry with this function
function add(_case, fn) {
callbacks[_case] = callbacks[_case] || [];
callbacks[_case].push(fn);
}
// this function work like switch(value)
// to make the name shorter you can name it `cond` (like in scheme)
function pseudoSwitch(value) {
if (callbacks[value]) {
callbacks[value].forEach(function(fn) {
fn();
});
}
}
and you can add new entry using:
add('something', function() {
// case for something
});
NOTE:
You can also modify this to work a little bit different than the original switch because you can have a function that returns a value and use a switch-like expression (like in Scheme where everything is an expression that returns a value):
const value = cond(42);
Writing this type of pseudoSwitch/cond function is left as an exercise to the reader.
NOTE 2:
By default objects in JavaScript use strings as keys and if you need to use something that can't be easily converted to a string, like objects (that will be converted to [Object object]) then you can use Map object that accepts anything as keys. Note that symbols work differently and they are not converted to a string when used as key in array.
This was the best/simpler solution for my needs:
const customSwitch = [
{
condition: 'case1',
fn() { /* Do stuff if case1 */ },
}, {
condition: 'canBeChangedAnytime',
fn() { /* Do stuff if case2 */ },
},
...adSomeCasesDynamycallyHere,
]
// edit a condition:
customSwitch[0].condition = 'changed';
// use the switch
for (const { condition, fn } of customSwitch) {
if (myValue === condition) {
fn();
break;
}
}
customSwitch, may have the form of an object, which may improve readability. Eg: customSwitch = { myCond: { condition, fn }}
You can click the above snippet to see it working ;)
const customSwitch = [ {
condition: 38,
fn: val => $("body").append(val === 38 ? 'UP' : 'RIGHT') + ' ',
}, {
condition: 40,
fn: val => $("body").append((val === 40 ? 'DOWN' : 'LEFT')+ ' ') ,
}]
$('#option1').click(function () {
customSwitch[0].condition = 38
customSwitch[1].condition = 40
});
$('#option2').click(function () {
customSwitch[0].condition = 39
customSwitch[1].condition = 37
});
$(window).keydown(function (e) {
for (const { condition, fn } of customSwitch) {
if (e.keyCode === condition) {
fn(e.keyCode);
break;
}
}
});
.btn {
cursor:pointer;
padding:5px;
border-radius:5px;
background-color:#3C0;
margin-top:5px;
width:150px;
text-align:center;
display:inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Press the four arrow keys:<br>
-if you click <b>option1</b> the switch will recognize UP and DOWN<br>
-if you click <b>option2</b> the switch will recognize LEFT and RIGHT<br>
<div id='option1' class='btn'>OPTION 1</div>
<div id='option2' class='btn'>OPTION 2</div>
<hr>
You can use Object for switch cases. One of the advantages of using Object instead of Array for the case is that it drastically reduces errors caused by wrong indexes in Array. Using Object for cases, you can also extract your case values into another script. This helps for Single Responsibility Principle by concerning you only implementing business logic inside switch cases instead of worrying about maintaining the right case values.
const OP = {
ADD: 'ADD',
MULTIPLY: 'MULTIPLY',
};
const choice = 'ADD';
switch (choice) {
case OP.ADD:
console.log('You chose add');
break;
case OP.MULTIPLY:
console.log('You chose multiply');
break;
default:
console.log('Operation is not defined');
}

Dynamically adding cases to a switch

I need to dynamically add cases to a switch. I want the user to be able to add items and every item needs it's own switch case.
You can use object with callback functions instead:
// you can have initial casses
var callbacks = {
'something': [() => 42]
};
// and you can create new entry with this function
function add(_case, fn) {
callbacks[_case] = callbacks[_case] || [];
callbacks[_case].push(fn);
}
// this function work like switch(value)
// to make the name shorter you can name it `cond` (like in scheme)
function pseudoSwitch(value) {
if (callbacks[value]) {
callbacks[value].forEach(function(fn) {
fn();
});
}
}
and you can add new entry using:
add('something', function() {
// case for something
});
NOTE:
You can also modify this to work a little bit different than the original switch because you can have a function that returns a value and use a switch-like expression (like in Scheme where everything is an expression that returns a value):
const value = cond(42);
Writing this type of pseudoSwitch/cond function is left as an exercise to the reader.
NOTE 2:
By default objects in JavaScript use strings as keys and if you need to use something that can't be easily converted to a string, like objects (that will be converted to [Object object]) then you can use Map object that accepts anything as keys. Note that symbols work differently and they are not converted to a string when used as key in array.
This was the best/simpler solution for my needs:
const customSwitch = [
{
condition: 'case1',
fn() { /* Do stuff if case1 */ },
}, {
condition: 'canBeChangedAnytime',
fn() { /* Do stuff if case2 */ },
},
...adSomeCasesDynamycallyHere,
]
// edit a condition:
customSwitch[0].condition = 'changed';
// use the switch
for (const { condition, fn } of customSwitch) {
if (myValue === condition) {
fn();
break;
}
}
customSwitch, may have the form of an object, which may improve readability. Eg: customSwitch = { myCond: { condition, fn }}
You can click the above snippet to see it working ;)
const customSwitch = [ {
condition: 38,
fn: val => $("body").append(val === 38 ? 'UP' : 'RIGHT') + ' ',
}, {
condition: 40,
fn: val => $("body").append((val === 40 ? 'DOWN' : 'LEFT')+ ' ') ,
}]
$('#option1').click(function () {
customSwitch[0].condition = 38
customSwitch[1].condition = 40
});
$('#option2').click(function () {
customSwitch[0].condition = 39
customSwitch[1].condition = 37
});
$(window).keydown(function (e) {
for (const { condition, fn } of customSwitch) {
if (e.keyCode === condition) {
fn(e.keyCode);
break;
}
}
});
.btn {
cursor:pointer;
padding:5px;
border-radius:5px;
background-color:#3C0;
margin-top:5px;
width:150px;
text-align:center;
display:inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Press the four arrow keys:<br>
-if you click <b>option1</b> the switch will recognize UP and DOWN<br>
-if you click <b>option2</b> the switch will recognize LEFT and RIGHT<br>
<div id='option1' class='btn'>OPTION 1</div>
<div id='option2' class='btn'>OPTION 2</div>
<hr>
You can use Object for switch cases. One of the advantages of using Object instead of Array for the case is that it drastically reduces errors caused by wrong indexes in Array. Using Object for cases, you can also extract your case values into another script. This helps for Single Responsibility Principle by concerning you only implementing business logic inside switch cases instead of worrying about maintaining the right case values.
const OP = {
ADD: 'ADD',
MULTIPLY: 'MULTIPLY',
};
const choice = 'ADD';
switch (choice) {
case OP.ADD:
console.log('You chose add');
break;
case OP.MULTIPLY:
console.log('You chose multiply');
break;
default:
console.log('Operation is not defined');
}

add a wildcard to a JavaScript switch statement

is there a way i can create a switch statement with a wildcard with the logic of:
case: '/jobs/'+WILDCARD and ending in +'-jobs' :
this is for the window.location.pathname, which could be '/jobs/design-jobs', or '/jobs/engineer-jobs' etc
but, there are other pages which start with '/jobs' I don't want this to apply to, e.g '/jobs/post'
or any suggestions on a better way?
No there are not wildcards for switch statements, but you could e.g. use RegExp and test against it:
if( path.match(/^\/jobs\/(.*)-jobs$/) !== null ) {
//jobs url
} else {
switch( path ) {
case '/jobs/post':
//something else
break;
}
}
One trick you can use in some cases could be to use a function to normalize the input of your switch, to turn variable inputs into the specific cases:
Instead of:
switch(input) {
case 'something': // something
case 'otherthing': // another
case '/jobs/'+WILDCARD: // special
}
You could do:
function transformInput (input) {
if (input.match(/jobs.*-jobs/) return 'JOBS';
return input;
}
switch(transformInput(input)) {
case 'something': // something
case 'otherthing': // another
case 'JOBS': // special
}
You can do something like this:
var categories = {
design: function(){ console.log('design'); },
engineer: function(){ console.log('engineer'); }
};
for(var category in categories)
if(window.location.pathname === '/jobs/' + category + '-jobs')
categories[category]();

Needing some visitor-like design pattern

I will give you a sample example of my problem to remove the logical complexity and let you be focus on the important part. Of course, this example will be a bit useless...
I have a tree structure where node are like that
{
path: "...",
childs : []
}
Now, I have to write all the full paths from root to each leaf in an array.
My design is very poor:
function listPaths(node) {
var result = [];
function listForNode(n, parentFullPath) {
var thisPath = parentFullPath + "/" + n.path;
result.push(thisPath);
n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
listForNode(node, "");
return result;
}
It could be nice but I can't write the test with Mocha without having an insane 600 line code test file. At this moment, you should be asking why. The reason is the complexity of the real purpose, that's not relevant for my question. My goal is to having something 'mockable' cause I'm used to. (Java dev). But I fail.
Do you have any pattern that I can use to resolve this one? I'm not really good at JS patterns. :/
Visitor? Making an Y Combinator? So many possibility...
Thank you for reading me
You need to remember that functions are first class citizens in javascript.
I see that essentially what you have is something like
function createVisitor(parentsAccumulatorInitialValue, parentsAccumulator){
var visitor = function myVisitor (node) {
var result;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
result.push(thisPath);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
result = [];
listForNode(node, parentsAccumulatorInitialValue());
return result;
}
return visitor;
}
var listPaths = createVisitor(
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
But that's not the only abstraction you could take care of:
function createVisitor2(
totalAccumulatorInitialValue,
totalAccumulator,
parentsAccumulatorInitialValue,
parentsAccumulator){
var visitor = function myVisitor (node) {
var total;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
total = totalAccumulator(total, thisPath, n);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
total = totalAccumulatorInitialValue();
listForNode(node, parentsAccumulatorInitialValue());
return total;
}
return visitor;
}
var listPaths2 = createVisitor2(
function totalInit() {
return [];
},
function totalAcc(total, thisPath, n){
total.push(thisPath);
return total;
},
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
Which might be pretty reasonable, but as you can see, I'm already beginning to have trouble finding appropriate names for these variables. In fact, I'd say the name of our function is bad, as doesn't create anything strictly like a visitor object I know of. However, it does work (BTW, I've slightly modified it to handle nulls as well as empty arrays):
> listPaths( { path:"foo",
childs: [{path:"bar", childs: null}, {path:"bob", childs: null}]})
["/foo", "/foo/bar", "/foo/bob"]
It can be modified even further so that your trees don't strictly even have the same structure... but we're already at 4 parameters, which isn't great. It'd be better if your visitor creator were passed a single extensible object with all the necessary methods or values. For instance, maybe (pseudocode):
function createVisitor3(opts) {
//assume we've defined GetDefaults() somewhere local to createVisitor3
// as well as assume that extend is defined somewhere that copies properties
// into a new object like various previously existing libraries do.
opts = extend({}, GetDefaults(), opts);
var totalAccumulatorInitialValue = opts.totalAccumulatorInitialValue;
var totalAccumulator = opts.totalAccumulator;
var parentsAccumulatorInitialValue = opts.parentsAccumulatorInitialValue;
var parentsAccumulator = opts.parentsAccumulator;
var childrenGetter = opts.childrenGetter;
/// etc.
...
}

"method overloading" in javascript

So I admit that I'm new to javascript and that I come from a C.+ background ("Hi, I'm Bob, I'm a class-based static language user", chorus "hi Bob!").
I find that I often end up writing functions like:
function someFunc()
{
if (arguments.length === 0 ){
...
} else {
...
}
}
(where there might be three such cases). Or, alternatively, I write the difference into the name:
function someFuncDefault() { ... };
function someFuncRealArg(theArg) { ... };
(Substitute "RealArg" for some semantically contentful phrase).
Is there a better pattern for this kind of thing?
Have a look at this post.
I don't know that I would do it this way, but it seems like it might make your code mildly less unmanageable:
function someFunc() {
switch (arguments.length) {
case 0: noArgs();
case 1: oneArg(arguments[0]);
case 2: twoArgs(arguments[0], arguments[1]);
}
function noArgs() {
// ...
}
function oneArg(a) {
// ...
}
function twoArgs(a, b) {
// ...
}
}
Another example might be:
function someFunc(a, b) {
if ('string' == typeof a) {
// ...
} else if ('number' == typeof a) {
// ...
}
}
And of course you can probably create something quite unmanageable by combining both examples (using conditions to determine behaviour based on number of arguments and types of arguments).
This is overloading, not overriding no?
Javascript is weakly typed, so method signatures and native support is out. My recommendation is to pass an extensible object as the solitary argument. Inspect and handle the existance of properties on the param object as you wish.
What advantage does this have over arguments? Well it lets you be explicit about your intentions where you call, and unambiguous about the meaning of arg1 and arg2 where you recieve, and it lets you abstract to a custom data object class you can extend functionality to.
function someFunc(params)
{
var x = params.x || defaultX;
var y = params.y || defaultY;
//businesslogic
}
someFunc({x:'foo',y:'bar'});
someFunc({y:'baz'});
In Javascript, all arguments are optional.
You might try something like:
Edit (better method that doesn't break for values whose 'truthiness' is false):
function bar(arg1, arg2) {
if(arg1 === undefined) { set default }
if(arg2 === undefined) { set default }
//do stuff here
}
Old method that breaks for falsey values:
function foo(arg1, arg2) {
if(!arg1) { set default }
if(!arg2) { set default }
//do stuff here
}
A great place to start with javascript are Douglas Crockford's javascript lectures: http://video.yahoo.com/search/?p=javascript
Javascript lets you get really lazy with this (not quite as lazy as Python, but pretty lazy).
function someFunc(arg1, arg2)
{
if(typeof(arg1) == "undefined") {
arg1 = default;
}
...
}
So you don't really need to overload. Javascript won't yell at you for passing the wrong number of parameters.
Everyone came close, I think that the real issue here is that in JavaScript you shouldn't change behavior based on parameters being passed in.
Since JavaScript makes all parameters optional you could follow the concise method of using this convention:
function foo(bar, baz) {
bar = bar || defaultBar;
baz = baz || defaultBaz;
.
.
.
}
Personally, I prefer to write the most complex function that would be performed, and then document it in comments so that others know that they do not have to send all the arguments.
//concat(str1, str2 [,str3 [,str4 [,str5]]])
function concat(str1, str2, str3, str4, str5) {
var str = str1 + str2;
if(str3 != undefined)
str += str3;
if(str4 != undefined)
str += str4;
if(str5 != undefined)
str += str5;
return str;
}
I have also found situations where the argument order would matter in a normal function, but sometimes I would want to sent the arguments seperately (i.e. I would want to send str3 and str5 but not str4). For this, I use an object and test the known properties
//concat({str1:string, str2:string, str3:string, str4:string, str5:string})
//str3, str4, and str5 are optional
function concat(strngs) {
var str = strngs.str1 + strngs.str2;
if(strngs.str3 != undefined)
str += strngs.str3;
if(strngs.str4 != undefined)
str += strngs.str4;
if(strngs.str5 != undefined)
str += strngs.str5;
return str;
}
A little more comprehensive overloading mechanism is offered by bob.js:
var notify = new bob.fn.overloadFunction([
{
condition: function(msg) { return bob.utils.isString(msg); },
overload: function(msg) {
console.log(msg);
}
},
{
condition: function(bSayHello) { return bob.utils.isBoolean(bSayHello); },
overload: function(bSayHello, msg) {
msg = bSayHello ? 'Hello: ' + msg : msg;
console.log(msg);
}
}
]);
Calling the overloaded function:
notify('Simple sentence.');
// Output:
// Simple sentence.
notify(true, 'Greeting sentence.');
// Output:
// Hello: Greeting sentence.
notify(123);
// JavaScript Error:
// "No matching overload found."

Categories

Resources