简体   繁体   中英

How should I deal with nested conditional statements?

I have a series of nested conditional checks and I am trying to come up with a clean solution and avoid if{}else{} hell as much as possible. Please see the example code as a demonstration of what I am trying to achieve.

In summary, I have two objects, CACHE_FILE_AGE_LIMIT and CACHE_FILE_CURRENT_AGE and I am trying to go through a nested age check:

first check the days,
    then check hours,
       then check minutes,
           then check seconds

The idea is that, the moment we reach a greater value, for example, days are equal, then we move on to hours and if we catch a greater hour value there, we end the check.

Please see the example for further info.

 const CACHE_FILE_AGE_LIMIT = { days: 3, hours: 6, minutes: 15, seconds: 57 } const CACHE_FILE_CURRENT_AGE = { days: 3, hours: 5, minutes: 14, seconds: 57 } function timeDiff(ageObj1, ageObj2) { console.log('days_limit', ageObj1.days, '?', 'days_age', ageObj2.days); // old: days document.getElementById("current_days").innerHTML = ageObj2.days; if (ageObj1.days < ageObj2.days) { console.log('old (days)'); document.getElementById("current_days").classList.add("red"); return true; } else { // equal: days if (ageObj1.days == ageObj2.days) { document.getElementById("current_hours").innerHTML = ageObj2.hours; console.log('\\tnext: hours'); console.log('\\thours_limit', ageObj1.hours, '?', 'hours_age', ageObj2.hours); if (ageObj1.hours < ageObj2.hours) { console.log('\\told (hours)'); document.getElementById("current_hours").classList.add("red"); return true; } else { // equal: hours if (ageObj1.hours == ageObj2.hours) { document.getElementById("current_minutes").innerHTML = ageObj2.minutes; console.log('\\t\\tnext (minutes)'); console.log('\\t\\tminutes_limit', ageObj1.minutes, '?', 'minutes_age', ageObj2.minutes); if (ageObj1.minutes < ageObj2.minutes) { // old: minutes console.log('\\t\\told (minutes)'); document.getElementById("current_minutes").classList.add("red"); return true; } else { // equal: minutes if (ageObj1.minutes == ageObj2.minutes) { document.getElementById("current_seconds").innerHTML = ageObj2.seconds; console.log('\\t\\t\\tnext (seconds)'); console.log('\\t\\t\\tseconds_limit', ageObj1.seconds, '?', 'seconds_age', ageObj2.seconds); if (ageObj1.seconds < ageObj2.seconds) { console.log('\\t\\t\\told (seconds)'); document.getElementById("current_seconds").classList.add("red"); return true; } else { console.log('\\t\\t\\tNOT old (seconds)'); document.getElementById("current_seconds").classList.add("blue"); return false; } } else { console.log('\\t\\tNOT old (minutes)'); return false; } } } else { console.log('\\tNOT old (hours)'); document.getElementById("current_hours").classList.add("blue"); return false; } } } else { console.log('NOT old (days)'); document.getElementById("current_days").classList.add("blue"); return false; } } } // Populate Limits var limit_fields = document.querySelectorAll(".limit"); for(i=0; i < limit_fields.length; i++){ //console.log('--->', i) let id = limit_fields[i].id.split("_")[1]; let val = CACHE_FILE_AGE_LIMIT[id] //console.log('\\tid:', id, 'val:', val); limit_fields[i].innerHTML = val; } // Evaluate Age document.getElementById("output").innerHTML = timeDiff(CACHE_FILE_AGE_LIMIT, CACHE_FILE_CURRENT_AGE) ? "old" : "up to date"; 
 .tg { border-collapse: collapse; border-spacing: 0; } .tg td { font-family: Arial, sans-serif; font-size: 12px; padding: 10px 5px; border-style: solid; border-width: 1px; overflow: hidden; word-break: normal; border-color: black; } .tg th { font-family: Arial, sans-serif; font-size: 12px; font-weight: normal; padding: 10px 5px; border-style: solid; border-width: 2px; overflow: hidden; border-color: black; } .tg .value { color: blue; text-align: center; vertical-align: top; } .tg .current { color: blue; text-align: center; vertical-align: top; } .tg .current.red { color: red; } .tg .current.blue { color: blue; } .tg .limit { color: #85929E; text-align: center; vertical-align: top; } .tg .header { background-color: #ffffc7; text-align: center; vertical-align: top; } .tg .item { background-color: #ffffc7; font-style: italic; text-align: right; vertical-align: top; } .tg .empty { background-color: #9b9b9b; text-align: right; vertical-align: top; } .tg .result { font-weight: bold; font-style: italic; background-color: #ffce93; text-align: right; vertical-align: top; } .tg .output { background-color: #FDEBD0; text-align: center; vertical-align: top; } 
 <table class="tg" style="undefined;table-layout: fixed; width: 265px"> <colgroup> <col style="width: 92px"> <col style="width: 92px"> <col style="width: 92px"> </colgroup> <tr> <th class="empty"></th> <th class="header">CURRENT</th> <th class="header">LIMITS</th> </tr> <tr> <td class="item">DAYS</td> <td id="current_days" class="value current">-</td> <td id="limit_days" class="value limit"></td> </tr> <tr> <td class="item">HOURS</td> <td id="current_hours" class="value current">-</td> <td id="limit_hours" class="value limit"></td> </tr> <tr> <td class="item">MINUTES</td> <td id="current_minutes" class="value current">-</td> <td id="limit_minutes" class="value limit"></td> </tr> <tr> <td class="item">SECONDS</td> <td id="current_seconds" class="value current">-</td> <td id="limit_seconds" class="value limit"></td> </tr> <tr> <td class="result">RESULT</td> <td id="output" class="output" colspan="2">up to date</td> </tr> </table> 

So as you can see, the conditional execution ends once it is clear that the hours are enough to determine the age.

Please change the values in the CACHE_FILE_CURRENT_AGE to test it.

--- UPDATE ---

There were multiple solutions so thanks to everyone for the help. Unfortunately I will have to pick up one of the answer to close the question.

Here is a demo of the solution:

在此处输入图片说明

So another possibility is to create something like a UNITS array, which contains simply the text representation of each property of your time-ish object, in the order you want to handle them. Then, simply use a while loop to iterate over that UNITS array, and update each DOM element in turn.

 const CACHE_FILE_AGE_LIMIT = { days: 3, hours: 6, minutes: 15, seconds: 57 }, CACHE_FILE_CURRENT_AGE = { days: 3, hours: 5, minutes: 17, seconds: 57 }, UNITS = ["days", "hours","minutes","seconds"] function timeDiff(ageObj1, ageObj2) { // We create a flag, and an index to iterate over our UNITS array let unitsIndex = 0; // if the upToDate flag is TRUE while(unitsIndex <= UNITS.length){ // We'll use template literals to create the IDs currentEl = document.getElementById(`current_${UNITS[unitsIndex]}`), limitEl = document.getElementById(`limit_${UNITS[unitsIndex]}`); console.log(UNITS[unitsIndex],ageObj1[UNITS[unitsIndex]],ageObj2[UNITS[unitsIndex]]) // Update the content of the current and limit elements currentEl.innerHTML = ageObj2[UNITS[unitsIndex]]; limitEl.innerHTML = ageObj1[UNITS[unitsIndex]]; // Here we check: is our limit unit less than our current? if (ageObj1[UNITS[unitsIndex]] < ageObj2[UNITS[unitsIndex]]) { /** * In this case, our current has exceeded our limit. Bad bad bad. **/ console.info(`The current ${UNITS[unitsIndex]} is stale.`) currentEl.classList.add("red"); return false; } else if(ageObj1[UNITS[unitsIndex]] > ageObj2[UNITS[unitsIndex]]){ /** * In this case, our limit is more than a full unit greater than our current. goodgoodgood. **/ console.info(`The current ${UNITS[unitsIndex]} is more than a full unit to the good.`) return true; } //increment our UNITS array pointer unitsIndex++; } // if we get here, then all the DHMS have passed -- we can return true return true; } // Populate Limits var limit_fields = document.querySelectorAll(".limit"); for(i=0; i < limit_fields.length; i++){ //console.log('--->', i) let id = limit_fields[i].id.split("_")[1]; let val = CACHE_FILE_AGE_LIMIT[id] //console.log('\\tid:', id, 'val:', val); limit_fields[i].innerHTML = val; } // Evaluate Age document.getElementById("output").innerHTML = timeDiff(CACHE_FILE_AGE_LIMIT, CACHE_FILE_CURRENT_AGE) ? "up to date" : "old"; 
 .tg { border-collapse: collapse; border-spacing: 0; } .tg td { font-family: Arial, sans-serif; font-size: 12px; padding: 10px 5px; border-style: solid; border-width: 1px; overflow: hidden; word-break: normal; border-color: black; } .tg th { font-family: Arial, sans-serif; font-size: 12px; font-weight: normal; padding: 10px 5px; border-style: solid; border-width: 2px; overflow: hidden; border-color: black; } .tg .value { color: blue; text-align: center; vertical-align: top; } .tg .current { color: blue; text-align: center; vertical-align: top; } .tg .current.red { color: red; } .tg .current.blue { color: blue; } .tg .limit { color: #85929E; text-align: center; vertical-align: top; } .tg .header { background-color: #ffffc7; text-align: center; vertical-align: top; } .tg .item { background-color: #ffffc7; font-style: italic; text-align: right; vertical-align: top; } .tg .empty { background-color: #9b9b9b; text-align: right; vertical-align: top; } .tg .result { font-weight: bold; font-style: italic; background-color: #ffce93; text-align: right; vertical-align: top; } .tg .output { background-color: #FDEBD0; text-align: center; vertical-align: top; } 
 <table class="tg" style="undefined;table-layout: fixed; width: 265px"> <colgroup> <col style="width: 92px"> <col style="width: 92px"> <col style="width: 92px"> </colgroup> <tr> <th class="empty"></th> <th class="header">CURRENT</th> <th class="header">LIMITS</th> </tr> <tr> <td class="item">DAYS</td> <td id="current_days" class="value current">-</td> <td id="limit_days" class="value limit"></td> </tr> <tr> <td class="item">HOURS</td> <td id="current_hours" class="value current">-</td> <td id="limit_hours" class="value limit"></td> </tr> <tr> <td class="item">MINUTES</td> <td id="current_minutes" class="value current">-</td> <td id="limit_minutes" class="value limit"></td> </tr> <tr> <td class="item">SECONDS</td> <td id="current_seconds" class="value current">-</td> <td id="limit_seconds" class="value limit"></td> </tr> <tr> <td class="result">RESULT</td> <td id="output" class="output" colspan="2">up to date</td> </tr> </table> 

Note that I'm using Template literals to define the id on each iteration:

`current_${UNITS[unitIndex] }`

By doing so, I don't have to hard-code the id every time. As long as I can trust the prefixes, this works pretty well. And it seems to get rid of a lot of your if statements, simply by running them each sequentially.

EDIT: I'm an idiot. So there are two branches to your if statement (well, three). First, if the current is beyond the limit, return false immediately, as that is OLD. Second, if the limit is more than a full unit greater than the current, immediately return true, as the current is well within its limit. Third, if they are equal, loop on to the next unit. If all units process and we fall through the while loop, then  they are the same exact. Weird, but still good. Code updated to reflect the SECOND case.

The only way, to transform nested conditions, to a linear representation of the code, is to move particels of single conditions into a function and use some for have a short circuit to exit the run. While you need to return a value, you need to store this flag, because the return value of some does not work for this.

The advanatge of this approach is to add more or less infinite conditions, break them as wanted and return a boolean value for further operations.

The disadvantage is, you need to have a look for data, the nested structure and an understanding, that the next condition/function is called until the function returnes true , despite the value of the flag.

 const CACHE_FILE_AGE_LIMIT = { days: 3, hours: 6, minutes: 15, seconds: 57 } const CACHE_FILE_CURRENT_AGE = { days: 3, hours: 5, minutes: 14, seconds: 57 } function timeDiff(ageObj1, ageObj2) { var flag; [ () => { document.getElementById("current_days").innerHTML = ageObj2.days; if (ageObj1.days < ageObj2.days) { document.getElementById("current_days").classList.add("red"); return flag = true; } }, () => { if (ageObj1.days == ageObj2.days) { document.getElementById("current_hours").innerHTML = ageObj2.hours; } else { document.getElementById("current_days").classList.add("blue"); flag = false; return true; } }, () => { if (ageObj1.hours < ageObj2.hours) { document.getElementById("current_hours").classList.add("red"); return flag = true; } }, () => { if (ageObj1.hours == ageObj2.hours) { document.getElementById("current_minutes").innerHTML = ageObj2.minutes; } else { document.getElementById("current_hours").classList.add("blue"); flag = false; return true; } }, () => { if (ageObj1.minutes < ageObj2.minutes) { document.getElementById("current_minutes").classList.add("red"); return flag = true; } }, () => { if (ageObj1.minutes == ageObj2.minutes) { document.getElementById("current_seconds").innerHTML = ageObj2.seconds; } else { flag = false; return true; } }, () => { if (ageObj1.seconds < ageObj2.seconds) { document.getElementById("current_seconds").classList.add("red"); return flag = true; } else { document.getElementById("current_seconds").classList.add("blue"); flag = false; return true; } } ].some(fn => fn()); return flag; } // Populate Limits var limit_fields = document.querySelectorAll(".limit"); for(i=0; i < limit_fields.length; i++){ //console.log('--->', i) let id = limit_fields[i].id.split("_")[1]; let val = CACHE_FILE_AGE_LIMIT[id] //console.log('\\tid:', id, 'val:', val); limit_fields[i].innerHTML = val; } // Evaluate Age document.getElementById("output").innerHTML = timeDiff(CACHE_FILE_AGE_LIMIT, CACHE_FILE_CURRENT_AGE) ? "old" : "up to date"; 
 .tg { border-collapse: collapse; border-spacing: 0; } .tg td { font-family: Arial, sans-serif; font-size: 12px; padding: 10px 5px; border-style: solid; border-width: 1px; overflow: hidden; word-break: normal; border-color: black; } .tg th { font-family: Arial, sans-serif; font-size: 12px; font-weight: normal; padding: 10px 5px; border-style: solid; border-width: 2px; overflow: hidden; border-color: black; } .tg .value { color: blue; text-align: center; vertical-align: top; } .tg .current { color: blue; text-align: center; vertical-align: top; } .tg .current.red { color: red; } .tg .current.blue { color: blue; } .tg .limit { color: #85929E; text-align: center; vertical-align: top; } .tg .header { background-color: #ffffc7; text-align: center; vertical-align: top; } .tg .item { background-color: #ffffc7; font-style: italic; text-align: right; vertical-align: top; } .tg .empty { background-color: #9b9b9b; text-align: right; vertical-align: top; } .tg .result { font-weight: bold; font-style: italic; background-color: #ffce93; text-align: right; vertical-align: top; } .tg .output { background-color: #FDEBD0; text-align: center; vertical-align: top; } 
 <table class="tg" style="undefined;table-layout: fixed; width: 265px"> <colgroup> <col style="width: 92px"> <col style="width: 92px"> <col style="width: 92px"> </colgroup> <tr> <th class="empty"></th> <th class="header">CURRENT</th> <th class="header">LIMITS</th> </tr> <tr> <td class="item">DAYS</td> <td id="current_days" class="value current">-</td> <td id="limit_days" class="value limit"></td> </tr> <tr> <td class="item">HOURS</td> <td id="current_hours" class="value current">-</td> <td id="limit_hours" class="value limit"></td> </tr> <tr> <td class="item">MINUTES</td> <td id="current_minutes" class="value current">-</td> <td id="limit_minutes" class="value limit"></td> </tr> <tr> <td class="item">SECONDS</td> <td id="current_seconds" class="value current">-</td> <td id="limit_seconds" class="value limit"></td> </tr> <tr> <td class="result">RESULT</td> <td id="output" class="output" colspan="2">up to date</td> </tr> </table> 

But you could take an array with keys and perform the until some parts returns true .

function timeDiff(ageObj1, ageObj2) {
    var flag;

    ['days', 'hours', 'minutes', 'seconds'].some((key, index, keys) => {
        var nextKey = keys[index + 1],
            element = document.getElementById("current_" + key),
            nextElement = document.getElementById("current_" + nextKey);

        element.innerHTML = ageObj2[key];
        if (ageObj1[key] < ageObj2[key]) {
            element.classList.add("red");
            return flag = true;
        }
        if (ageObj1[key] == ageObj2[key] && nextKey) {
            nextElement.innerHTML = ageObj2[nextKey];
        } else {
            element.classList.add("blue");
            flag = false;
            return true;
        }
    });

    return flag;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM