简体   繁体   English

我应该如何处理嵌套条件语句?

[英]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. 我进行了一系列嵌套的条件检查,并尝试提出一个干净的解决方案,并尽可能避免if{}else{}地狱。 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: 总而言之,我有两个对象CACHE_FILE_AGE_LIMITCACHE_FILE_CURRENT_AGE并且我正在尝试进行嵌套的年龄检查:

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. 如您所见,一旦很明显hours足以确定年龄,则有条件执行结束。

Please change the values in the CACHE_FILE_CURRENT_AGE to test it. 请更改CACHE_FILE_CURRENT_AGE的值以CACHE_FILE_CURRENT_AGE进行测试。

--- 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. 因此,另一种可能性是创建类似UNITS数组的对象,该数组仅包含您时事对象的每个属性的文本表示形式(按您要处理它们的顺序)。 Then, simply use a while loop to iterate over that UNITS array, and update each DOM element in turn. 然后,只需使用while循环来迭代该UNITS数组,然后依次更新每个DOM元素。

 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: 请注意,我使用模板文字来定义每次迭代的id:

`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. 将嵌套条件转换为代码的线性表示的唯一方法是将单个条件的微粒移动到函数中,并使用some短路导致退出运行。 While you need to return a value, you need to store this flag, because the return value of some does not work for this. 当您需要返回一个值时,您需要存储此标志,因为some返回值对此不起作用。

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. 缺点是,您需要查找数据,嵌套结构和理解,即调用下一个条件/函数,直到该函数返回true为止,而不管标志的值如何。

 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 . 但是您可以使用带有键的数组并执行直到某些部分返回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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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