I am basically trying to avoid branching. I will explain my doubt with the code:
My code is like:
name = "any_random_name_generated_dynamically"
//If else conditions
// foo and bar are the part of the name not the complete name
if (name.match('foo_pattern')) {
"do_thing_1"
}
else if (name.match('bar_pattern')) {
"do_thing_2"
}
else {
"do_thing_3"
}
Is there anyway I can avoid this using javascript objects? Or there any other way to do where less branching is involved.
I tried googling it bu didn't find anything for this particular issue.
You could create a resolve table:
var resolve=[
["Some pattern",function(){alert("wohoo");}],
...
["",function(){alert("default")}]
];
So now you can simply iterate through the table, and find the first pattern that matches, and execute its related function:
var name="somestring";
resolve.find(el=>name.match(el[0]))[1]();
Note that this will crash if no pattern is found, so take care to add a default one.
Alternatively you could use a switch (still ugly):
switch(true){
case name.match("sth") :
dosth();
break;
default:
dodefault();
break;
}
Related
I have a code to generate math problems with random numbers. I am using switch statement to choose which task should be generated.
function genTask(taskid) {
switch (taskid) {
case 1:
// generate some numbers
return numbers;
break;
case 2:
// generate some numbers
return numbers;
break;
// ...
}
}
I think there may be some performance issues when I add 150+ cases. Does this code go trough every case? Wouldnt it be faster if there are separate functions for every task?
function task1() {
// generate some numbers
return numbers;
}
function task2() {
// ...
}
function genTask(taskid) {
switch (taskid) {
case 1:
return task1();
break;
case 2:
return task2();
break;
// ...
}
}
Is there a faster way to do this?
First of all, you need to know where you need to use if/else or switch/case.
When you need to check 2 to 3 conditions then you can use if/elseif/else
When you need to check 5 or above then definitely use switch/case
And based on speed switch/case is faster then if/else
Let's get back to the original point,
In your case, you have 2 choices
Write all code in one switch case.
Make functions in chunk and call-in switch case.
I suggest you go with the second choice because switch case also a faster way of execution compared to other conditional checks, But when you make different functions then you can easily modify it and debug it which more help in development, and performance not compromised in that case.
One more approach is you can use a object lookup
function task1() {
// generate some numbers
return numbers;
}
function task2() {
// some task
}
const taskMap = { 1: task1, 2: task2 };
function genTask(taskid, defaultVal) {
return (taskMap[taskid] && taskMap[taskid]()) || defaultVal;
}
This will be simple object lookup, though in terms of performance it might be slower than switch case but it increases resuability and readability of the code.
In general, I think the performance of "array" is better than "if/else" or "switch". See the reference below. In your specific case, if you comparing if/else to switch, then switch is better.
Using functions will not affect the performance ( I think ), but it is better and preferable as the code will be cleaner and readable.
Reference : https://www.oreilly.com/library/view/high-performance-javascript/9781449382308/ch04.html
While I go into my use-case in (hopefully not too much) detail below, keep in mind that the fundamental question is "How do I create a customized function at runtime in javascript?"
I have (potentially large) arrays of objects, and the user can build custom searches on those objects. The search function is passed an array of filters of the form
[{field:'name', predicate:'contains', modifier:'Joe'},
{field:'type', predicate:'is', modifier:'Boss'}]
which in this example would return all bosses named Joe.
Toward that end, I created a filtering function that applies the filters to the master list, which looks something like:
var matches = everythingOfThatType;
whereClause.forEach(function(filter) {
switch(filter.predicate) {
case '=':
case 'is':
matches = matches.filter(function(record) { return record[filter.field] == filter.modifier; });
console.log(filter, matches);
break;
case '!=':
case 'is not':
matches = matches.filter(function(record) { return record[filter.field] != filter.modifier; });
console.log(filter, matches);
break;
...
and so on.
It's working all right, but now I'm getting some gnarly complexity (special rules for filter combinations, recursive filtering on array properties, etc.), and for performance it would be better to only iterate through the list of all candidates once anyway.
my ideal solution would look something like
var filterFunc = magicallyCreateFilterFunc(filters);
var matches = everythingOfThatType.filter(filterFunc);
where magicallyCreateFilterFunc() would have something like the original switch statement, but instead of applying the filter, would add a line to the function that would eventually be applied to all the objects. Then I can add all the other complexity and recursion and whatnot in a tight, efficient manner.
It seems to me that JavaScript is well-suited for this sort of task (as an aside, the deeper I get into JavaScript the more I appreciate its depth), but I'm a little stuck on step 1: dynamically define a function based on data at run-time. I would really appreciate a nudge in the right direction.
It turns out there is a simple, clean way to do this. What I didn't know when I asked this question oh, so long ago is that closures and filters are best pals.
Rather than apply the filters in the switch statement, I can add the filter functions to an array, then use a closure to execute all the functions on each member of the array.
So my switch statement above looks more like
var buildFilterList = function(whereClause) {
var filterFunctions = [];
whereClause.forEach(function(filter) {
switch(filter.predicate) {
case '=':
case 'is':
filterFunctions.push((function(field) { return function(record) { return record[filter.field] == filter.modifier; })})(field));
break;
...
Which gives me a list of functions to apply to each element, each in a closure that contains the field it should be applied to. More complex filter functions could have more parameters. Now, how to efficiently take that list of filters and apply them? Closures again.
var filterApplicator = function(filters) {
return function(item) {
var passed = true, i = 0, filterCount = filters.length;
for (i = 0; passed && i < filterCount; i++) {
passed = filters[i](item);
}
return passed;
}
}
var filterFunctions = buildFilterList(whereClause);
matches = everythingOfThatType.filter(filterApplicator(filterFunctions));
filterApplicator() returns a function that will be applied to each element in the original array. That function is in a closure that includes the array of filter functions, so all it has to do is loop through those functions and apply them on the element until one fails.
(It should be noted that I have not tested this exact syntax, but the basic concept is what I wanted to pass on here.)
I have a requirement that the user can provide arbitrary statements which can be stored in a function and called later to get a return value. A simple example of this is that userInput might be
var x = 10;
x;
I would store this via
var callback = function() {
return eval(userInput);
}
and then running callback() returns 10 as expected.
However, I also need to support the case with an explicit return statement, ie userInput might be
var x = 10;
return x;
In this case the eval method above will fail with SyntaxError: return not in function. Instead I could store callback as
var callback = new Function(userInput);
My issue is that I would like to combine these two approaches according the rule 'get explicit return value otherwise get the result of the last executed statement'. In particular this can't be done with analysis of the code at callback creation time as the user could potentially do something odd like
if(envVar < 10)
return a;
b * 0.5;
which combines both.
Any thoughts on how to structure the creation of the callback function to accommodate these possible inputs? Unfortunately it is not possible to enforce one style or the other on the user.
UPDATE to answer some of the comments below.
One solution suggested is to search for a return token and choose between new Function and eval. This doesn't work for my last example, see http://jsfiddle.net/ZGb6z/2/ - out4 should be "no" but ends up being undefined as the last executed statement is not returned.
Another suggestion is to modify the user input to add an explicit return on the last line if one is not found. Unfortunately it's not possible to know which statement will be executed last. Consider
var x = 10;
switch(x) {
case 10:
100;
break;
default:
200;
break;
}
When called it should return 100, but it would take some serious analysis to determine where to put returns for arbitrary code.
Just use a try catch, manipulating the input will be very painful for you, and try catch can't really make your code any more obtuse at this point.
var failback = function () {
try {
return eval(userInput);
} catch (e) {
return Function(userInput);
}
};
What I would really suggest is investing in a parser, kind of like how Angular does it. That kind of thing would prevent your users from doing whatever the hell they want, introducing attack vectors, yadda, yadda, yadda.
Either manage your expectations or manage your user's expectations. eval and new Function() are not suitable for your requirements if you require mixed usage of explicit and non-explicit return statements in the same user-input. You will continue to find issues following either of these routes.
Simply searching for the word return is not sufficient either... var returning = true; or var a = 'return'; or /* return true */ true; will all throw false positives.
Managing your expectations: To do such a thing you require a form of lexer and parser, at which point you can do away with eval entirely and execute your own safe functions based on the parsed input. This is the best approach when execution of user input has to occur anyway as you can ensure that nothing gets executed you do not wish to permit. If you want to cover these sort of edge cases and permit strange user input then you must be prepared to increase the size and development time of your application. I have built a few applications executing user generated code and have always come to the conclusion this is the correct route to go down.
Managing your user's expectations: Provide a guide, tell them not to mix explicit returns with non-explicit returns, these are strange coding practices anyway. Better yet explicitly tell them to include or omit the return statement. There is no shame in asking your users to follow them, especially if it allows you to improve their experience elsewhere.
There I was thinking I'd only see problems like this at the code golf stack exchange :)
My solution is here: http://jsfiddle.net/hKq87/1
It essentially replaces the 'return' statement with an exception that has a special string prefixed to it. If we see that string, we know we are actually returning a value, and return it rather than re-raising the exception.
The reason I chose to throw an exception rather than replace the return statement with a function call was because it is hard to know where the JS code evaluated for the return really ends. It could be split across multiple lines, contain several special characters and may not even have the optional semi-colon at the end. So I concatenate a string to whatever the value being returned is and throw it, as the throw keyword doesn't require it's argument to be wrapped in parentheses.
In addition, throwing exceptions provides me a convenient way to immediately terminate execution of the code block, without halting other JS execution.
Here is the callback method:
var callback = function(userInput) {
var returned = undefined;
userInput = userInput.replace(/(^|[\(\\)[\]{};,\s])return(\s*[^\s;])?/gi, function(m, b, e){
return b + " throw '__RETURNED_VALUE'" +
(e !== undefined ? "+" + e : "");
});
try {
returned = eval(userInput);
} catch (e) {
if (e.indexOf("__RETURNED_VALUE") == 0) {
returned = e.substring("__RETURNED_VALUE".length) || undefined;
}
else {
throw e;
}
}
return returned;
}
The regex above accounts for variables that may end with the string "return", that we would not want to replace as it is not a return statement. It also allows for return statements within braces, without trailing semi-colons or at the very beginning/end.
One issue with the current method is that you can not use the (rare) comma operator in a return statement, or expect numerical values to be returned correctly. The last test case in the jsfiddle demonstrates this. An example from here:
//original
return 5 * 2 + 3, 22;
//modified
throw '__RETURNED_VALUE='+ 5 * 2 + 3, 22;
//apply * operator
throw '__RETURNED_VALUE='+ 10 + 3, 22;
//apply + operators
throw '__RETURNED_VALUE=103', 22;
//apply , operator
throw 22;
This problem can be avoided by completely eliminating the prefix '__RETURNED_VALUE=' and just replacing 'return' with 'throw'. However, this means that the code provided must run without throwing exceptions, which I thought to be a harder constraint than just crafting return statements to be simple (avoiding comma operators, non-parenthesized arithmetic, etc.). In addition, if a user ever creates a return statement that we can't handle with the current code, we conveniently throw an exception for it so it easily comes to our attention.
jsFiddle Demo
Lets assume your user can be a little smarter than the average bear. We are going to ask them to specifically provide an initial value and then a callback with an expression for that value.
The main benefit of doing this is that you avoid eval, and actually have a nice implementation that is re-usable as opposed to being subject to later refactoring.
This way also provides a separation of where the input comes from and where the examination comes from. Although the provided example only shows integer input, really it could be another call with absolutely no knowledge of the value aside that it needs to conform to the callback logic.
function expression(x,callback){
return callback(x);
}
out1.innerHTML = expression(8,function(x){
return x;
});
out2.innerHTML = expression(10,function(x){
return x;
});
out3.innerHTML = expression(10,function(x){
if(x === 10) return "yes"; "no";
});
out4.innerHTML = expression(8,function(x){
return x === 10 ? "yes" : "no";
});
this is my first post ever on stackoverflow! I am a front-end web developer enthusiast and novice...If I am breaching the stackoverflow etiquette or rules of the road please let me know or give me the smack down..
I am trying to evaluate class names in an array of elements. If the class name contains a certain value then I want to manipulate an attribute for that element.
First, I create an array of elements using jquery stored as a variable:
note that buttons class naming convention is "share_button_[social media service name]"
Next, I create a for loop to iterate through the buttons variable
Within the for loop I have switch statement - the purpose is to evaluate each element in the Buttons array and add an href attribute to the element if it meets a certain criteria
Putting it all together:
var buttons = $('a[class^="share_button"]');
for (i=0; i < buttons.length; i++) {
switch (true) {
case ($(buttons[i]).attr('[class*="twitter"]')):
console.log('twitter!');
break;
case ($(buttons[i]).attr('[class*="linkedin"]')):
console.log('linkedin!');
break;
case ($(buttons[i]).attr('[class*="facebook"]')):
console.log('facebook_like!');
break;
case ($(buttons[i]).attr('[class*="google_plusone"]')):
console.log('google_plusone!');
break;
case ($(buttons[i]).attr('[class*="reddit"]')):
console.log('reddit!');
break;
}
}
This does not seem to be working at all. Here is the codepen, http://cdpn.io/fKoak
Is it a good practice to loop through a jquery array of elements like this?
Should I be using the switch statement in this case and am I using it correctly? (there are more possible cases then I have case statements for and I have no default - I want the cases without a match to "do noting")
In this particular case, what i wrong with the formation of my code that the desired outcome is not happening?
I think it would be better to do something more like this.
var $buttons = $('a[class^="share_button"]');
var $twitterButtons = $('[class*="twitter"]', $buttons);
$twitterButtons.each(function(i, button) {
//Do stuff to the twitter button
});
var $linkedinButtons = $('[class*="linkedin"]', $buttons);
$linkedinButtons.each(function(i, button) {
//Do stuff to the linkedin button
});
var $facebookButtons = $('[class*="facebook"]', $buttons);
$facebookButtons.each(function(i, button) {
//Do stuff to the facebook button
});
var $google_plusoneButtons = $('[class*="google_plusone"]', $buttons);
$google_plusoneButtons.each(function(i, button) {
//Do stuff to the google_plusone button
});
var $redditButtons = $('[class*="reddit"]', $buttons);
$redditButtons.each(function(i, button) {
//Do stuff to the reddit button
});
Adding the second parameter to your selectors gives them a context. So $('[class*="twitter"]', $buttons) looks through $buttons and selects those with a class containing twitter
You can use jQuery's each() method to iterate over the elements, then check the className
$('a[class^="share_button"]').each(function(i, elem) {
if ( elem.className.indexOf('twitter') != -1 ) {
console.log('twitter');
}else if ( elem.className.indexOf('linkedin') != -1 ) {
console.log('linkedin');
}else if (..... etc
});
A better approach would be to keep the switch, but google_plusone kinda screws that up with the underscore, so you'd have to replace that with something else:
$('a[class^="share_button"]').each(function(i, elem) {
switch( elem.className.split('_').pop() ) {
case 'twitter' :
console.log('twitter');
break;
case 'linkedin' :
console.log('linkedin');
break;
case 'googleplusone' :
// you'll have to remove the underscore or just check for "plusone"
}
});
I ultimately decided to drop the for loop and use jquery .each method -http://api.jquery.com/each/ - that was recommended by #adeno above. The two solutions offered by #adeno using .each both work good but I finally chose to go with the jquery .is method https://stackoverflow.com/a/2240085 - http://api.jquery.com/is/ - since we decided to use .each method it is already a "jquery solution" so using .is method to evaluate if the class name of each element contained a certain value was a lot less code - and allowed for more flexibility then the proposed .indexOf and .split/.pop methods by #adeno in imo..
#Tom also gave a workable solution. However, although I didn't mention it specifically in my question, I wanted a solution that would use an iterator to go through the array of selected button elements.
var $buttons = $('a[class^="share_button"]');
$buttons.each(function(i,e){
switch (true) {
case ($(e).is('[class*="twitter"]')):
alert('yea! it\'s a twitter button - now do something');
break;
case ($(e).is('[class*="linkedin"]')):
alert('yea! it\'s a linkedin button - now do something');
break;
case ($(e).is('[class*="facebook"]')):
alert('yea! it\'s a faceboook button - now do something');
break;
case ($(e).is('[class*="google_plusone"]')):
alert('yeah! it\'s a google plus one button - now do something');
break;
case ($(e).is('[class*="reddit"]')):
alert('yea! it\'s a reddit one button - now do something');
break;
}
});
I was trying to use a if...else statement with arrays without having to declare the arrays. I got it to work this way:
if(['banana','apple','lemon'].indexOf(somevar) >-1)
{
//code
}
else if(['chicken','dog','elephant'].indexOf(somevar) >-1)
{
//code
}
.
.
.
And it keep going this way until some dozens of if...elses. The code is working fine, no problem noticed. Is it a bad pratice? Is it really an array? Can it cause some performance loss, memory leaks, or reference problems? Does the "not declared" array used in this code, if it is really an array, have a proper name in programming?
It seems pointless in my opinion, since you know exactly what element you want.
As for memory, the array should be deallocated as soon as you move to the next statement, and I would personally consider this bad practice in this instance, since, like I said earlier, it doesn't do anything since you know which will be selected.
If it's a static list that the user is going to select an element from, this is alright but I would probably define the array elsewhere and just reference that when needed, so you don't have to create the exact same array over and over again.
I consider it bad practice since if I wanted to add/remove/change an element in an array, I would rather just change the array when it's declared at the top, or change the data source. By sprinkling it through your code, you allow the possibility of hard to maintain code.
How about
switch(somevar) {
case 'banana': case 'apple': case 'lemon'
//...
break;
case 'chicken': case 'dog': case 'elephant'
//...
break;
}
You're just declaring/using arrays on-the-fly. Nothing really wrong with it, it's just one of many coding styles. e.g.
if (somevar == 'banana') || (somevar == 'apple') || etc...) {
...code...
} else if (somevar == 'chicken') || (somevar == 'dog') || etc...) {
... code
}
or perhaps
switch(somevar) {
case 'banana':// fall-through
case 'apple': // fall-through
case ...
.... code ...
break;
case 'chicken':
case 'dog':
etc...
}
They're all valid. It comes down to what your project's code style guidelines are and how many of these comparisons you need to do.
I see no problem, but I would declare these arrays for readability and maintenance:
var fruits = ['banana','apple','lemon'],
animals = ['chicken','dog','elephant'];
if(fruits.indexOf(somevar) > -1)
{
//code
}
else if(animals.indexOf(somevar) > -1)
{
//code
}
Now it's clearer why you check if someVar is in one array or the other, and it's easier to update the arrays - I want add another animal, I go to the animals array, not "the first else if block".
Yes, ['banana','apple','lemon'] is an array, but your code will fail when somevar === 'chicken' because ['banana','apple','lemon'].indexOf(somevar) === 0, which is a falsey value.
Also, your else if statement is redundant. You should check the indices by doing:
if(['banana','apple','lemon'].indexOf(somevar) >= 0 ) { ... }