Imagine that I have a variable called incomingValue and I'm getting a number from an API as it's value. The values are between 0 to 1 and I'm setting two other variables depending on this value using bunch of if statements like you see below.
var incomingValue; // Set by an API
var setValueName;
var setValueIcon;
if (incomingValue < 0.10 ) {
setValueName = 'something';
setValueIcon = 'something.png'
}
if (incomingValue > 0.09 && incomingValue < 0.20 ) {
setValueName = 'somethingElse';
setValueIcon = 'somethingElse.png';
}
In the actual implementation, I have around 10 if statements checking for specific intervals up until 1. e.g. do this if it's more than 0.10 but less than 0.16 and so on.
As a JavaScript beginner it feels like this is not the right way to do things even though it gets the job done. How would I refactor this code?
Update: As requested, I'm adding the full set of intervals that are used in the original code. I haven't included the full list before because the intervals don't follow a certain pattern.
So, how do you refactor multiple nested if statements? The easiest possible way is to use guard clauses. A guard clause is an if statement that checks for a condition and favors early exit from the current method. If the condition is satisfied, the if block returns from the method.
Remember the single responsibility principle. Take that code out to a separate function like so:
function determineNameAndIcon(incomingValue) {
if (incomingValue < 0.10) {
return {
name: "something",
icon: "something.png"
};
}
if (incomingValue < 0.20) {
return {
name: "somethingElse",
icon: "somethingElse.png"
};
}
// etc
return {
name: "somethingDefault",
icon: "somethingDefault.png"
};
}
// ...
var incomingValue; // Set by an API
const {
name: setValueName,
icon: setValueIcon
} = determineNameAndIcon(incomingValue);
Notice that determineNameAndIcon
is still a very long function with repeating parts. This can be further refactored to a data-driven version:
const nameAndIconList = [
{
maxValue: 0.10,
name: "something",
icon: "something.png"
},
{
maxValue: 0.20,
name: "somethingElse",
icon: "somethingElse.png"
},
// ...
];
const nameAndIconDefault = {
name: "somethingDefault",
icon: "somethingDefault.png"
};
function determineNameAndIcon(incomingValue) {
for (let item of nameAndIconList) {
if (incomingValue < item.maxValue) {
return item;
}
}
return nameAndIconDefault;
}
function findValue(incomingValue){
var itemValues = [
[.06, "valueName", "valueIcon"],
[.08, "valueName", "valueIcon"],
[.09, "valueName", "valueIcon"],
[.1, "valueName", "valueIcon"],
]
var foundValues = itemValues.
filter(v=>v[0] >= incomingValue)
.sort();
if(foundValues.length == 0){
throw "incoming value not found."
}
return foundValues[0];
}
let value = findValue(.079);
console.log( value );
This is assuming that you want the lowest portion of the range to be the one selected (just reverse the sort if you want it to be the highest).
A solution using an array where you set the ranges for your results:
var categories = [{something: [0, 0.1]},
{somethingElse: [0.1, 0.2]},
{somethingElse2: [0.2, 0.3]},
{anotherSomething: [0.3, 1]}];
function res(number){ return Object.keys(categories.filter(function(elem) {
var key = elem[Object.keys(elem)];
return number >= key[0] && number < key[1]
})[0])[0]};
var incomingValue = 0.12;
var setValueName = res(incomingValue);
var setValueIcon = res(incomingValue) + ".png";
console.log(setValueName, setValueIcon);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With