I have one function which is having if elseif conditions and the cyclomatic complexity is approaching 5. How do I reduce it?
function testFunc() {
var step = getModel('step');
if(step === 1) {
this.resetTask(); //calling some function
this.updateStep(0);
return true;
} else if(step === 2) {
this.initTask; //some other function
return true;
} else if(step === 3) {
this.name === 'add' ? this.add() : this.edit();
return true;
}
return false;
}
tried replacing with switch case but it didn't help.
Very simple refactor - remove all conditional logic you have now and extract each piece as a separate function into a map. Since you only execute one branch each time, depending on step, you can make that value the key and fetch what needs to be executed. You can then provide a fallback for when there is nothing that corresponds to step.
Now the cyclomatic complexity is 2, since there is only one place the code branches - either you find the corresponding handler for step or not. Also, the branching in step 3 is a completely separate function now, thus it doesn't need to count as part of testFunc
function testFunc() {
var step = getModel('step');
var steps = {
1: function() {
this.editTask(); //calling some function
this.updateStep(0);
return true;
},
2: function() {
this.initTask; //some other function
return true;
},
3: function() {
this.name === 'add' ? this.add() : this.edit();
return true;
},
default: function() {
return false;
}
};
var fn = steps[step] || steps.default;
return fn();
}
This will reduce your code complexity and makes more readable.
You can decouple your condition into multiple function and attach that function in condition object. here I'm passing this in argument but you can use different approch. here I've also given value of this just for the shake of running example. copy and paste in your editor and try.
function meaningfulFuncName1() {
this.editTask(); //calling some function
this.updateStep(0);
return true;
}
function meaningfulFuncName2() {
this.initTask; //some other function
return true;
}
function meaningfulFuncName3(context) {
context.name === 'add' ? context.add() : context.edit();
return true;
}
function defaultCase() {
return false;
}
function testFunc() {
this.name = 'add'
const step = getModel('step')
conditionObject = {
1: meaningfulFuncName1,
2: meaningfulFuncName2,
3: meaningfulFuncName3,
default: defaultCase
}
return conditionObject[step](this);
}
To me, changing it like this makes it less complex to understand. Although to some will be less readable. Also it is a little faster.
function testFunc() {
var step = getModel('step');
if(step==1){this.editTask(); this.updateStep(0); return true;}
if(step==2){this.initTask; return true;}
if(step==3){this.name==='add' ? this.add() : this.edit();return true;}
return false;
}
Related
How do I break out of a jQuery each loop?
I have tried:
return false;
in the loop but this did not work. Any ideas?
Update 9/5/2020
I put the return false; in the wrong place. When I put it inside the loop everything worked.
To break a $.each or $(selector).each loop, you have to return false in the loop callback.
Returning true skips to the next iteration, equivalent to a continue in a normal loop.
$.each(array, function(key, value) {
if(value === "foo") {
return false; // breaks
}
});
// or
$(selector).each(function() {
if (condition) {
return false;
}
});
According to the documentation return false; should do the job.
We can break the $.each() loop [..] by making the callback function
return false.
Return false in the callback:
function callback(indexInArray, valueOfElement) {
var booleanKeepGoing;
this; // == valueOfElement (casted to Object)
return booleanKeepGoing; // optional, unless false
// and want to stop looping
}
BTW, continue works like this:
Returning non-false is the same as a continue statement in a for loop; it will skip immediately to the next iteration.
I came across the situation where I met a condition that broke the loop, however the code after the .each() function still executed. I then set a flag to "true" with an immediate check for the flag after the .each() function to ensure the code that followed was not executed.
$('.groupName').each(function() {
if($(this).text() == groupname){
alert('This group already exists');
breakOut = true;
return false;
}
});
if(breakOut) {
breakOut = false;
return false;
}
I created a Fiddle for the answer to this question because the accepted answer is incorrect plus this is the first StackOverflow thread returned from Google regarding this question.
To break out of a $.each you must use return false;
Here is a Fiddle proving it:
http://jsfiddle.net/9XqRy/
I know its quite an old question but I didn't see any answer, which clarify that why and when its possible to break with return.
I would like to explain it with 2 simple examples:
1. Example:
In this case, we have a simple iteration and we want to break with return true, if we can find the three.
function canFindThree() {
for(var i = 0; i < 5; i++) {
if(i === 3) {
return true;
}
}
}
if we call this function, it will simply return the true.
2. Example
In this case, we want to iterate with jquery's each function, which takes anonymous function as parameter.
function canFindThree() {
var result = false;
$.each([1, 2, 3, 4, 5], function(key, value) {
if(value === 3) {
result = true;
return false; //This will only exit the anonymous function and stop the iteration immediatelly.
}
});
return result; //This will exit the function with return true;
}
"each" uses callback function.
Callback function execute irrespective of the calling function,so it is not possible to return to calling function from callback function.
use for loop if you have to stop the loop execution based on some condition and remain in to the same function.
I use this way (for example):
$(document).on('click', '#save', function () {
var cont = true;
$('.field').each(function () {
if ($(this).val() === '') {
alert('Please fill out all fields');
cont = false;
return false;
}
});
if (cont === false) {
return false;
}
/* commands block */
});
if cont isn't false runs commands block
When I search for a function name - it gets clumsy when I see plenty of function names as in the example. In large code base I waste my time when searching for how and from where a function has been called :
function do_something()
{
if (typeof do_something.flag == "undefined")
{
do_something.flag = true;
}
if (do_something.flag == null)
{
do_something.flag = true;
}
}
Here when I search for do_something so that I can look from where it is called instead I find plenty of lines consisting of do_something.flag1,do_something.flag2 and so on which isn't of any use in most of such searches. In large function I get plenty of such lines occupying search output.
I've another scenario. In (Netbeans) IDE I want to do Ctrl-F do_something function looking for where it is called in the file. Now I find pressing F3 within the function itself iterating over it's own lines containing something like do_something.var1=5 etc.
In short is there any way to reduce the function name usage within the function when creating object-global variables?
I've much longer functions but I'll give real example of medium level function causing this problem:
function slow_down_clicks(label, userfunc, timeinmsec)
{
console.log("slow_down_clicks=" + label);
if (typeof slow_down_clicks.myobj == UNDEFINED)
{
slow_down_clicks.myobj = {};
}
if (typeof slow_down_clicks.myobj[label] == UNDEFINED)
{
slow_down_clicks.myobj[label] = {
inqueue: false,
lastclickedtime: 0,
login_post_error_count: 0
};
}
var myfunc = function ()
{
console.log("Executing the user func");
slow_down_clicks.myobj[label].inqueue = false;
slow_down_clicks.myobj[label].lastclickedtime = new Date().getTime();
userfunc();
}
console.log("Due to error in home.aspx reloading it", ++slow_down_clicks.myobj[label].login_post_error_count);
if (slow_down_clicks.myobj[label].inqueue == false)
{
var diff = new Date().getTime() - slow_down_clicks.myobj[label].lastclickedtime;
console.log("diff=", diff, timeinmsec);
if (diff > timeinmsec)
{
myfunc(); //click login
}
else
{
console.log("queuing the request after:", timeinmsec - diff);
slow_down_clicks.myobj[label].inqueue = true;
setTimeout(function ()
{
console.log("called myfunc babalatec");
myfunc();
}, timeinmsec - diff);
}
}
else
{
console.log("Discarding this request...");
}
}
I think you can just define the fields as normal variables and put your code in its own file. Then you can just refer to the variables by its name inside your function because they are within the function's closure. The variables will not be accessible outside because you limit them in its own file.
like:
let flag = false
function doSomething () {
if (!flag) {
flag = true
}
....
}
Put the above code snippet in a separate file and import the function when you want to use it.
How do I break out of a jQuery each loop?
I have tried:
return false;
in the loop but this did not work. Any ideas?
Update 9/5/2020
I put the return false; in the wrong place. When I put it inside the loop everything worked.
To break a $.each or $(selector).each loop, you have to return false in the loop callback.
Returning true skips to the next iteration, equivalent to a continue in a normal loop.
$.each(array, function(key, value) {
if(value === "foo") {
return false; // breaks
}
});
// or
$(selector).each(function() {
if (condition) {
return false;
}
});
According to the documentation return false; should do the job.
We can break the $.each() loop [..] by making the callback function
return false.
Return false in the callback:
function callback(indexInArray, valueOfElement) {
var booleanKeepGoing;
this; // == valueOfElement (casted to Object)
return booleanKeepGoing; // optional, unless false
// and want to stop looping
}
BTW, continue works like this:
Returning non-false is the same as a continue statement in a for loop; it will skip immediately to the next iteration.
I came across the situation where I met a condition that broke the loop, however the code after the .each() function still executed. I then set a flag to "true" with an immediate check for the flag after the .each() function to ensure the code that followed was not executed.
$('.groupName').each(function() {
if($(this).text() == groupname){
alert('This group already exists');
breakOut = true;
return false;
}
});
if(breakOut) {
breakOut = false;
return false;
}
I created a Fiddle for the answer to this question because the accepted answer is incorrect plus this is the first StackOverflow thread returned from Google regarding this question.
To break out of a $.each you must use return false;
Here is a Fiddle proving it:
http://jsfiddle.net/9XqRy/
I know its quite an old question but I didn't see any answer, which clarify that why and when its possible to break with return.
I would like to explain it with 2 simple examples:
1. Example:
In this case, we have a simple iteration and we want to break with return true, if we can find the three.
function canFindThree() {
for(var i = 0; i < 5; i++) {
if(i === 3) {
return true;
}
}
}
if we call this function, it will simply return the true.
2. Example
In this case, we want to iterate with jquery's each function, which takes anonymous function as parameter.
function canFindThree() {
var result = false;
$.each([1, 2, 3, 4, 5], function(key, value) {
if(value === 3) {
result = true;
return false; //This will only exit the anonymous function and stop the iteration immediatelly.
}
});
return result; //This will exit the function with return true;
}
"each" uses callback function.
Callback function execute irrespective of the calling function,so it is not possible to return to calling function from callback function.
use for loop if you have to stop the loop execution based on some condition and remain in to the same function.
I use this way (for example):
$(document).on('click', '#save', function () {
var cont = true;
$('.field').each(function () {
if ($(this).val() === '') {
alert('Please fill out all fields');
cont = false;
return false;
}
});
if (cont === false) {
return false;
}
/* commands block */
});
if cont isn't false runs commands block
I need to pass certain parameters into a function and have that function pull from an array based on the arguments passed to it. It's hard to explain, so I'll show you what I'm trying to do.
function SearchDeck(deck,...){
var tryagain = true;
do{
if(deck[0].property == value){
//do something;
tryagain = false;
}
else{
deck.splice(0,1);
}
}
while(tryagain);
}
There are multiple decks to look in, the proper deck will be passed in. I want to always be drawing off the top of the deck (index 0 of the array). I need to draw continuously until I find a card that matches what I'm after. I splice out the 0 index if it doesn't match. What I'm after is dynamic, varying across the properties or even the operators I would use.
Some examples of if statements I would have are...
deck[0].color == "orange"
deck[0].value >= 5
deck[0].value < -4
I could make multiple functions or have the function fork based on an argument, but that doesn't seem like the best way to go about this.
If I'm understanding this correctly, you want the behavior of the if(deck[0].property == value) to be different for each invocation of the SearchDeck(...) function?
My recommendation would be to pass in a function:
function SearchDeck(deck, validationFunction, ...){
var tryagain = true;
do{
if(validationFunction(deck[0])){
//do something;
tryagain = false;
}
else{
deck.splice(0,1);
}
}
while(tryagain);
}
Then when you call the code, you can do:
SearchDeck(deck, function(firstCard) { return firstCard.color == "orange" }, ...);
SearchDeck(deck, function(firstCard) { return firstCard.value >= 5 }, ...);
SearchDeck(deck, function(firstCard) { return firstCard.value < -4 }, ...);
Or, if the cases you're looking for might be reused, it might also be cleaner to make those named functions:
function validateColor(firstCard) {
return firstCard.color == "orange";
}
function validateHighValue(firstCard) {
return firstCard.value >= 5;
}
function validateLowValue(firstCard) {
return firstCard.value < -4;
}
SearchDeck(deck, validateColor, ...);
SearchDeck(deck, validateHighValue, ...);
SearchDeck(deck, validateLowValue, ...);
It sounds like you may be interested in the typeof operator:
if (typeof deck == 'object') { ... }
if (typeof deck[0].color == 'string') { ... }
if (typeof deck[0].value == 'number') { ... }
Alternatively:
if (deck[0].hasOwnProperty('color')) { ... }
This is what I came up with. You need to push the way of check (either "== 'oragne'" or "<3") as string.
function searchDeck() {
var deck = Array.prototype.slice.call(arguments, 1),
tryagain = true,
string = deck[deck.length - 1];
deck.pop();
while (tryagain) {
if (eval('deck[0].property' + string)) {
//do something;
alert('card found');
tryagain = false;
} else {
deck.splice(0, 1);
}
}
}
Hope this is what you wanted ;)
Here is a working jsfiddle example, of course there might be a more elegant way, this is just what I came up with. Note that eval can be dangerous, so you should be carefull if user picks what the test will be (the string pushed into the array).
I see different topics about the toggle function in jquery, but what is now really the best way to toggle between functions?
Is there maybe some way to do it so i don't have to garbage collect all my toggle scripts?
Some of the examples are:
var first=true;
function toggle() {
if(first) {
first= false;
// function 1
}
else {
first=true;
// function 2
}
}
And
var first=true;
function toggle() {
if(first) {
// function 1
}
else {
// function 2
}
first = !first;
}
And
var first=true;
function toggle() {
(first) ? function_1() : function_2();
first != first;
}
function function_1(){}
function function_2(){}
return an new function
var foo = (function(){
var condition
, body
body = function () {
if(condition){
//thing here
} else {
//other things here
}
}
return body
}())`
Best really depends on the criteria your application demands. This might not be the best way to this is certainly a cute way to do it:
function toggler(a, b) {
var current;
return function() {
current = current === a ? b : a;
current();
}
}
var myToggle = toggler(function_1, function_2);
myToggle(); // executes function_1
myToggle(); // executes function_2
myToggle(); // executes function_1
It's an old question but i'd like to contribute too..
Sometimes in large project i have allot of toggle scripts and use global variables to determine if it is toggled or not. So those variables needs to garbage collect for organizing variables, like if i maybe use the same variable name somehow or things like that
You could try something like this..: (using your first example)
function toggle() {
var self = arguments.callee;
if (self.first === true) {
self.first = false;
// function 1
}
else {
self.first = true;
// function 2
}
}
Without a global variable. I just added the property first to the function scope.
This way can be used the same property name for other toggle functions too.
Warning: arguments.callee is forbidden in 'strict mode'
Otherwise you may directly assign the first property to the function using directly the function name
function toggle() {
if (toggle.first === true) {
toggle.first = false;
// function 1
}
else {
toggle.first = true;
// function 2
}
}