如何优化if多层判断

小猴纸
  • 3
新手上路,请多包涵
aa(item) {
    let color;
    let eq1 = "";
    let eq2 = "";

    for (let i = 0; i < this.option.columnTow.length; i++) {
        let element = this.option.columnTow[i];
        console.log(parseInt(eq1) < parseInt(item.value))
        if (eq1 != "" && eq2 != "" && parseFloat(eq1) < parseFloat(item.value) && parseFloat(item.value) < parseFloat(eq2)) {
            color = element.prop;
            break;
        }
        else if (eq1 != "" && eq2 != "" && parseFloat(eq2) < parseFloat(eq1)) {
            if (element.equation == "大于" && parseFloat(item.value) > parseFloat(element.value)) {
                color = element.prop;
                break;
            }
            else if (element.equation == "小于" && parseFloat(item.value) < parseFloat(element.value)) {
                color = element.prop;
                break;
            }
        }
        if (element.equation == "大于" && eq2 == "" && parseFloat(item.value) > parseFloat(element.value)) {
            color = element.prop;
            break;
        }
        if (element.equation == "小于" && eq1 == "" && parseFloat(item.value) < parseFloat(element.value)) {
            color = element.prop;
            break;
        }
        if (element.equation == "等于" && parseFloat(item.value) == parseFloat(element.value)) {
            color = element.prop;
            break;
        }
    }
    return color;
}
编者 @边城 :像这种代码,可以格式化一下再按 Markdown 的代码语法来粘贴。

像这种多层if判断的如何优化简洁,感谢

回复
阅读 743
5 个回答
  1. function aafunction 没写,我复制到IDE报错了。
  2. 整体看下来逻辑不复杂,循环中判断条并修改color,打断循环,返回color

    function aa(item) {
     let color;
     let eq1 = "";
     let eq2 = "";
    
     for (let i = 0; i < this.option.columnTow.length; i++) {
         let element = this.option.columnTow[i];
         console.log(parseInt(eq1) < parseInt(item.value))
         if (条件1) { color = element.prop; break; }
         else if (条件2) {
             if (条件3) { color = element.prop;  break; }
             else if (条件4) { color = element.prop; break; }
         }
         if (条件5) { color = element.prop; break; }
         if (条件6) { color = element.prop; break; }
         if (条件7) { color = element.prop; break; }
     }
     return color;
    }

    那实际上并不需要打断这个动作,直接返回值即可,也就是这段可以简化为

    function aa(item) {
     let eq1 = "";
     let eq2 = "";
    
     for (let i = 0; i < this.option.columnTow.length; i++) {
         let element = this.option.columnTow[i];
         console.log(parseInt(eq1) < parseInt(item.value))
         if (条件1) return element.prop;
         if (条件2 && 条件3) return element.prop;
         if (条件2 && 条件4) return element.prop;
         if (条件5) return element.prop;
         if (条件6) return element.prop;
         if (条件7) return element.prop;
     }
    }
  3. 条件具备相似性,可以合并同类项。

     let 条件1 = eq1 != "" && eq2 != "" && parseFloat(eq1) < parseFloat(item.value) && parseFloat(item.value) < parseFloat(eq2)
     let 条件2 = eq1 != "" && eq2 != "" && parseFloat(eq2) < parseFloat(eq1)
     let 条件3 = element.equation == "大于" && parseFloat(item.value) > parseFloat(element.value)
     let 条件4 = element.equation == "小于" && parseFloat(item.value) < parseFloat(element.value)
     let 条件5 = element.equation == "大于" && eq2 == "" && parseFloat(item.value) > parseFloat(element.value)
     let 条件6 = element.equation == "小于" && eq1 == "" && parseFloat(item.value) < parseFloat(element.value)
     let 条件7 = element.equation == "等于" && parseFloat(item.value) == parseFloat(element.value)

    一个是判断eq1和eq2,另一个是item.value 和 element.value,这部分简化下来就是

    
     let 条件10 = eq1 == ""
     let 条件11 = eq2 == ""
     let 条件12 = parseFloat(item.value) - parseFloat(element.value)
     let 条件13 = parseFloat(eq2) < parseFloat(eq1)
     let 条件14 = element.equation
    
     let 条件1 = !条件10 && !条件11 && !条件13 && parseFloat(eq1) < parseFloat(item.value) && parseFloat(item.value) < parseFloat(eq2)
     let 条件3 = !条件10 && !条件11 && 条件13 && 条件14 == "大于" && 条件12 >0
     let 条件4 = !条件10 && !条件11 && 条件13 && 条件14 == "小于" && 条件12 <0
     let 条件5 = 条件14 == "大于" && 条件11 && 条件12 > 0
     let 条件6 = 条件14 == "小于" && 条件10 && 条件12 < 0
     let 条件7 = 条件14 == "等于" && 条件12 == 0

    并且可以看出条件1到条件7是互异的,也就是其中一个成立,其他的肯定都是不成立的。所以顺序并不影响判断,我们调整顺序并用或链接起来

    function aa(item) {
     let eq1 = "";
     let eq2 = "";
    
     for (let i = 0; i < this.option.columnTow.length; i++) {
         let element = this.option.columnTow[i];
         let 条件10 = eq1 == ""
         let 条件11 = eq2 == ""
         let 条件12 = parseFloat(item.value) - parseFloat(element.value)
         let 条件13 = parseFloat(eq2) < parseFloat(eq1)
         let 条件14 = element.equation
         if (
             条件14 == "等于" && 条件12 == 0 ||
             条件10 ? (
                 条件14 == "小于" && 条件12 < 0
             ) : (
                 条件11 ? (
                     条件14 == "大于" && 条件12 > 0
                 ) : (
                     条件13 ? (
                         条件14 == "大于" && 条件12 > 0
                         || 条件14 == "小于" && 条件12 < 0
                     ) : (
                         parseFloat(eq1) < parseFloat(item.value) && parseFloat(item.value) < parseFloat(eq2)
                     )
                 )
             )
         ) return element.prop;
     }
     return null
    }

    如果能将条件14 == "大于" && 条件12 > 0 封装起来,会更加利于简化。

好的注释能让屎山变成巧克力味的屎山

先分析一下源代码

aa(item) {
    let color;
    let eq1 = "";
    let eq2 = "";

    for (let i = 0; i < this.option.columnTow.length; i++) {
        let element = this.option.columnTow[i];
        console.log(parseInt(eq1) < parseInt(item.value))
        // 逻辑1:如果 eq1 和 eq2 都有指定,判断 item.value 是否在它们之间
        if (eq1 != "" && eq2 != "" && parseFloat(eq1) < parseFloat(item.value) && parseFloat(item.value) < parseFloat(eq2)) {
            color = element.prop;
            break;
        }
        // 逻辑2:如果 eq1 和 eq2 都有值,但无效(假设 eq1 <= eq2 为有效),
        // 直接按 element.equation 判断 item.value 和 element.value 的大小
        else if (eq1 != "" && eq2 != "" && parseFloat(eq2) < parseFloat(eq1)) {
            if (element.equation == "大于" && parseFloat(item.value) > parseFloat(element.value)) {
                color = element.prop;
                break;
            }
            else if (element.equation == "小于" && parseFloat(item.value) < parseFloat(element.value)) {
                color = element.prop;
                break;
            }
        }
        
        // 剩下的逻辑,① eq1 和 eq2 都有值,且有效;② eq1 和 eq2 不都有值
        
        // 逻辑3:若没有 eq2,即没有最大值,item.value > element.value 算,前提是 equation
        if (element.equation == "大于" && eq2 == "" && parseFloat(item.value) > parseFloat(element.value)) {
            color = element.prop;
            break;
        }
        
        // 逻辑4:若没有 eq1,即没有最小值,item.value < element.value 算,前提是 equation
        if (element.equation == "小于" && eq1 == "" && parseFloat(item.value) < parseFloat(element.value)) {
            color = element.prop;
            break;
        }
        
        // 逻辑5:若 euqation 是等于,判断 item.value 和 element.value 相等
        if (element.equation == "等于" && parseFloat(item.value) == parseFloat(element.value)) {
            color = element.prop;
            break;
        }
    }
    return color;
}

总结规律:

  1. 判断 item 和 element 大小关系的时候,都跟 equation 有关,这里可以写一个函数

    function predicate(item, element, ...types) {
        // 如果 element.equalltion 不在指定要进行的比较类型中,直接失败
        if (!types.includes(element.equaltion)) {
            return false;
        }
    
        const [iValue, eValue] =  [item.value, element.value].map(s => parseFloat(s));
        switch (element.equaltion) {
            case "大于": return iValue > eValue;
            case "小于": return iValue < eValue;
            case "等于": return iValue === eValue;
            default: return false;
        }
    }
  2. 可以把 eq1 和 eq2 看成 min 和 max,意义更明确
  3. \( min < item < max \) 的时候,跟 element.value 没有关系,只需要取第 1 个 element 的 prop
  4. \( max < min \) 的时候,只判断 item 和 element 的大/小关系,不判等
  5. 没有 max 的时候,判大于
  6. 没有 min 的时候,判小于
  7. 其他情况,判等于

由于 item.value、eq1 和 eq2 的值都会多处用到,可以先 parse 出来。另外,由于 predicate 函数所在范围内有 itemValue 值,所以不需要传入第 1 个参数

aa() {
    const [minValue, maxValue, itemValue] = [eq1, eq2, item.value].map(s => parseFloat(s));

    function predicate(element, ...types) {
        if (!types.includes(element.equaltion)) {
            return false;
        }

        const eValue = parseFloat(element.value);
        switch (element.equaltion) {
            case "大于": return itemValue > eValue;
            case "小于": return itemValue < eValue;
            case "等于": return itemValue === eValue;
            default: return false;
        }
    }

    // 判断某个 element 是否符合条件
    function isValidElement(element) {
        if (eq1 !== "" && eq2 !== "") {
            if (itemValue > minValue && itemValue < maxValue) {
                return true;
            }
            if (maxValue < minValue && predicate(element, "大于", "小于")) {
                return true;
            }
        }

        if (eq2 === "" && predicate(element, "大于")) {
            return true;
        }
        if (eq1 === "" && predicate(element, "小于")) {
            return true;
        }

        return predicate(element, "等于");
    }

    // 对 this.option.columnTow 进行判断,找到符合条件的 element
    const element = this.option.columnTow.find(isValidElement);

    // 最后还是有可能没找到,会返回 undefined
    return  element?.prop;
}

最后声明:分析出来的逻辑难免有失误,需要经过逻辑验证和测试来证明它是正确的

基本上有两个方向:扁平化具体化

扁平化

提前中断,比如这一段:

    if (eq1 !="" && eq2 !="" &&   parseFloat(eq1) < parseFloat(item.value) && parseFloat(item.value)  < parseFloat(eq2) ){
        color= element.prop;
        break;
    }
    else if

第一个 if 判断如果通过,就不会走后面的逻辑,所以可以改成:

if (xxx) {
  color = element.prop;
  break;
}
// 后面的计算
if (ooo) {

}
...

记得把特殊情况导致中断的代码往前方。

具体化

每个条件都很长,很难读,这个时候可以用具体的名字来总结,比注释更好维护。

比如:

const isMeathillGoodLookAndKindHearted = meathill 
  && meathill.isGoodLook() 
  && meathill.isKindHearted();
if (isMeathillGoodLookAndKindHearted) {
  giveMoneyTo(meathill);
}
let color;
    let eq1 = "";
    let eq2 = "";

    for (let i = 0; i < this.option.columnTow.length; i++) {
        let element = this.option.columnTow[i];
        let eq1Num = parseFloat(eq1);
        let eq2Num = parseFloat(eq2);
        let itemValNum = parseFloat(item.value);
        let eleValNum = parseFloat(element.value);
        if (
            (eq1 && eq2 && eq1Num < itemValNum && itemValNum < eq2Num) ||
            (eq1 &&
                eq2 &&
                eq2Num < eq1Num &&
                ((element.equation == "大于" && itemValNum > eleValNum) ||
                    (element.equation == "小于" && itemValNum < eleValNum))) ||
            (element.equation == "大于" && eq2 && itemValNum > eleValNum) ||
            (element.equation == "小于" && eq1 && itemValNum < eleValNum) ||
            (element.equation == "等于" && itemValNum == eleValNum)
        ) {
            color = element.prop;
            break;
        }
    }
    return color;
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏