How to reduce cognitive complexity of nested if - javascript

I have an if statement set up like this
if (A && B) {
// do something 1
} else {
if (B) {
// set some boolean to false
}
// do something 2
}
I'm wondering if I can lower the cognitive complexity? Right now this is a score of 4.

I would say the best way to lower the cognitive complexity is to use functions. This is similar to #GuerricP original answer, but handles the multiple case of do somthing 2
eg.
function doSomething2() {}
if (A && B) {
// do something 1
} else if (B) {
// set some boolean to false
doSomething2();
} else {
doSomething2();
}
This reduces complexity, because it's not obvious that there are 2 routes to doSomething2 in your original version.

Well you could have only one level of depth like this:
function originalImplementation(A, B) {
if (A && B) {
console.log("do something 1");
} else {
if (B) {
console.log("set some boolean to false");
}
console.log("do something 2");
}
}
function newImplementation(A, B) {
if (A && B) {
console.log("do something 1");
}
else if (B) {
console.log("set some boolean to false");
}
if (!A || !B) {
console.log("do something 2");
}
}
console.log("originalImplementation");
originalImplementation(0, 0);
originalImplementation(0, 1);
originalImplementation(1, 0);
originalImplementation(1, 1);
console.log("newImplementation");
newImplementation(0, 0);
newImplementation(0, 1);
newImplementation(1, 0);
newImplementation(1, 1);

I think this is the right way to do it and the cleanest.
const DoSomething = function(){}
if (A && B) {
}
else if (B) {
DoSomething();
}
else {
DoSomething();
}

Assuming you do one and only one thing for each case you can try decluttering the syntax:
One-liner if statements don't need the curly braces
You can avoid if, else if, etc. with an early return
const run = (a, b) => {
if (a && b) return fun1();
if (a) return fun2();
if (b) return fun3();
return fun4();
}
In this case I prefer using nested ternaries. Something usually considered as "bad practice" by tool makers and opinion leaders in the industry but I think with the right indentation they offer more decluttering opportunities:
const run = (a, b) =>
( a && b ? fun1()
: a ? fun2()
: b ? fun3()
: fun4());
Of course YMMV ;)

Setting the boolean first can setup a clearer if/else
if(!A && B) {
// set some boolean to false
}
if (A && B) {
// do something 1
} else {
// do something 2
}
Another strategy is to drop out of functions asap
e.g.
if(X) {
// do stuff
return;
}
if(Z)
{
// do different stuff
return;
}
// do default stuff
return;
This allows the reader to dismiss logic beyond the condition they are interested in
Finally you can also create functions with meaningful names rather than comments
if(X) {
return doSomething2();
}

Related

can't complete the function on chainshot

i'm new to js and started trying to learn code with chainshot
there's a simple task that I can't pass:
Task description
Let's complete the isEqual function! If a is equal to b return true.
my code
function isEqual(a, b) {
if (a === b) {
console.log(" true ");
}
}
export default isEqual;
You just need to return true, instead of using console.log, and return false if a wasnt equal to b. console.log doesnt return a value, it just shows it on the console, as it's name suggest. Use the keyword return for that, and return the keyword true or false instead of a string, since you're expecting a boolean.
function isEqual(a, b) {
if (a === b) {
return true;
} else {
return false;
}
}
export default isEqual;
You can simplify the function by just returning a === b, like
function isEqual(a, b) {
return a === b;
}
export default isEqual;
Your function should return boolean value. But in your code function doesn't return any.
function isEqual(a, b) {
return a === b;
}
export default isEqual;
What website are you running that on? It worked for me.
Here is a simpler version of the code you're trying to do:
function isEqual(a, b) {
return a === b
}

reduce complexity of if elseif conditions

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;
}

Javascript micro optimisation for if statement

I am fairly new to JavaScript and I have a question regarding how to optimise if statements.
I will show you two scenarios.
//first
var number = 10;
var calculationOneResult = functionOne(number);
var calculationTwoResult = functionTwo(number);
if (calculationOneResult === true) {
//stuff
} else if (calculationTwoResult === true) {
//more stuffs
}
//second
var number = 10;
if (functionOne(number) === true) {
//stuff
} else if (functionTwo(number) === true) {
//more stuffs
}
Here is my question:
In the first scenario, I am calculating two times.
In the second one, if the first function returns true, will it calculate the second elseif statement or will it skip it after doing the stuff ?
The following code:
if(statement1) {
// stuff
} else if(statement2) {
// other stuff
}
is equivalent to
if(statement1) {
// stuff
} else {
if(statement2) {
// other stuff
}
}
as there is no elseif in JavaScript - see documentation.
So the answer is any function in statement2 will be simply skipped.
Nothing in an else clause executes if the if expression tests as true, so the second version of your code will definitely save a function call in such cases.

Difference between "trampoline" methods

I've started learning JS recently, and this is my first dive into functional language territory.
Was doing the "trampoline" exercise in the functional-javascript-workshop npm module, and came across an interesting difference between my own solution and the official solution. Both work perfectly fine, but I'm at a loss as to what exactly is the practical difference between them. I understand my own solution pretty well, but don't exactly understand why the other one works too.
My Solution
function repeat(op, num) {
var _repeat = function() {
if (num <= 0) return;
op();
return repeat(op, --num);
};
return _repeat;
}
function trampoline(f) {
while (f instanceof Function) {
f = f();
}
return f;
}
module.exports = function(op, num) {
return trampoline(repeat(op, num));
};
Official Solution
function repeat(operation, num) {
return function() {
if (num <= 0) return
operation()
return repeat(operation, --num)
}
}
function trampoline(fn) {
while(fn && typeof fn === 'function') {
fn = fn()
}
}
module.exports = function(operation, num) {
trampoline(function() {
return repeat(operation, num)
})
}
Specifically, I'm curious about the last part - why does the official solution create an annonymous function instead of just passing repeat?
There's not really a reason.
Except maybe that the trampoline will make the all calls, while in your version the first invocation of repeat is done outside of it. This might be considered cleaner, and could make an actual difference when trampoline was more sophisticated (e.g. wrapping all execution in a try-catch).
Take a look at the trampoline:
function trampoline(fn) {
while(fn && typeof fn === 'function') {
fn = fn()
}
}
Notice how it keeps iterating as long asfn is a function, which it invokes.
So theoretically you can have as many nested functions and get the same result:
module.exports = function(operation, num) {
trampoline(function() {
return function() {
return function() {
return repeat(operation, num);
};
};
});
};
Demo: http://jsbin.com/yixaliyoye/1/edit
The Official solution has one more redundant step than your solution. No real reason for it, other than the original author probably thought it looked more readable.

How Would I Make an If Statement From Arguments in Javascript?

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).

Categories