这JS判断怎么简化,把我给看晕了

kaipizhe
  • 1.3k
const isDel = (op, o) => {
    let fal = false;
    if (op.is_show_lock && op.is_show_sf && op.is_show_bz) {
        if (o.is_lock && o.is_sf && o.is_bz) {
            fal = false;
        } else {
            fal = true;
        }
    } else if (op.is_show_lock && op.is_show_sf) {
        if (o.is_lock && o.is_sf) {
            fal = false;
        } else {
            fal = true;
        }
    } else if (op.is_show_lock && op.is_show_bz) {
        if (o.is_lock && o.is_bz) {
            fal = false;
        } else {
            fal = true;
        }
    } else if (op.is_show_sf && op.is_show_bz) {
        if (o.is_sf && o.is_bz) {
            fal = false;
        } else {
            fal = true;
        }
    } else if (op.is_show_lock) {
        if (o.is_lock) {
            fal = false;
        } else {
            fal = true;
        }
    } else if (op.is_show_sf) {
        if (o.is_sf) {
            fal = false;
        } else {
            fal = true;
        }
    } else if (op.is_show_bz) {
        if (o.is_bz) {
            fal = false;
        } else {
            fal = true;
        }
    }
    return fal;
};

----------------------完整-----------------------------

<template>
<div style="margin-bottom: 20px; margin-top: 20px;">
    <label><input type="checkbox" v-model="show_gl.is_show_gl1" /> 过滤一</label>
    <label><input type="checkbox" v-model="show_gl.is_show_gl2" /> 过滤二</label>
    <label><input type="checkbox" v-model="show_gl.is_show_gl3" /> 过滤三</label>
</div>
<div>
    <table>
        <template v-for="(item, index) in list" :key="index">
            <tr v-if="item.is_show">
                <td>{{item.id}}</td>
                <td>{{item.is_gl1}}</td>
                <td>{{item.is_gl2}}</td>
                <td>{{item.is_gl3}}</td>
            </tr>
        </template>
    </table>
</div>
</template>

<script>
import {reactive, ref, watch} from "vue";

export default {
    name: "Test",
    setup() {
        const show_gl = reactive({
            is_show_gl1: false,
            is_show_gl2: false,
            is_show_gl3: false,
        });

        const list = ref([
            {
                id: 1,
                is_gl1: false,
                is_gl2: true,
                is_gl3: false,
                is_show: true,
            },
            {
                id: 2,
                is_gl1: false,
                is_gl2: false,
                is_gl3: true,
                is_show: true,
            },
            {
                id: 3,
                is_gl1: false,
                is_gl2: true,
                is_gl3: false,
                is_show: true,
            },
            {
                id: 4,
                is_gl1: true,
                is_gl2: true,
                is_gl3: true,
                is_show: true,
            },
            {
                id: 5,
                is_gl1: false,
                is_gl2: true,
                is_gl3: false,
                is_show: true,
            },
            {
                id: 6,
                is_gl1: true,
                is_gl2: false,
                is_gl3: false,
                is_show: true,
            },
        ]);

        watch(show_gl, () => {
            for (let i = 0; i < list.value.length; i++) {
                if (show_gl.is_show_gl1 || show_gl.is_show_gl2 || show_gl.is_show_gl3) {
                    if (show_gl.is_show_gl1 && show_gl.is_show_gl2 && show_gl.is_show_gl3) {
                        if (list.value[i].is_gl1 && list.value[i].is_gl2 && list.value[i].is_gl3) {
                            list.value[i].is_show = true;
                        } else {
                            list.value[i].is_show = false;
                        }
                    } else if (show_gl.is_show_gl1 && show_gl.is_show_gl2) {
                        if (list.value[i].is_gl1 && list.value[i].is_gl2) {
                            list.value[i].is_show = true;
                        } else {
                            list.value[i].is_show = false;
                        }
                    } else if (show_gl.is_show_gl1 && show_gl.is_show_gl3) {
                        if (list.value[i].is_gl1 && list.value[i].is_gl3) {
                            list.value[i].is_show = true;
                        } else {
                            list.value[i].is_show = false;
                        }
                    } else if (show_gl.is_show_gl2 && show_gl.is_show_gl3) {
                        if (list.value[i].is_gl2 && list.value[i].is_gl3) {
                            list.value[i].is_show = true;
                        } else {
                            list.value[i].is_show = false;
                        }
                    } else if (show_gl.is_show_gl1) {
                        if (list.value[i].is_gl1) {
                            list.value[i].is_show = true;
                        } else {
                            list.value[i].is_show = false;
                        }
                    } else if (show_gl.is_show_gl2) {
                        if (list.value[i].is_gl2) {
                            list.value[i].is_show = true;
                        } else {
                            list.value[i].is_show = false;
                        }
                    } else if (show_gl.is_show_gl3) {
                        if (list.value[i].is_gl3) {
                            list.value[i].is_show = true;
                        } else {
                            list.value[i].is_show = false;
                        }
                    }
                } else {
                    list.value[i].is_show = true;
                }
            }
        });

        return {show_gl, list}
    },

}
</script>

<style scoped>
table, td {
    border: solid 1px #666;
}
td {
     padding: 5px 10px;
}
label {
    margin-right: 20px;
}
</style>

其实就是一个过滤功能

回复
阅读 5.1k
7 个回答
const isDel = (op, o) => {
      // 查询字段关联
      const dicWhereRel = {
        is_show_gl1: "is_gl1",
        is_show_gl2: "is_gl2",
        is_show_gl3: "is_gl3"
      };
      //filter 筛选需要的查询字段; every 字段值是否相等,都相等为true,否则为false
      return Object.entries(op)
        .filter(([key, val]) => val)
        .every(([key, val]) => o[dicWhereRel[key]] == val);
    };

    Vue.watch(show_gl, () => {
      list.value.forEach((item) => {
        item.is_show = isDel(show_gl, item);
      });
    });

https://codepen.io/1567887123...

2021-08-17 更新

昨天有点忙,只留了代码。今天来补个解释。

观察问题,总结规律:

  • op.is_show_xxxo.is_xxx 成对出现
  • op.is_show_xxxtrue 的时候需要判断 o.is_xxx (过滤)
  • 将过滤出来的所有 o.is_xxx&& 组合条件,得到计算结果
  • 最终结果正好与计算结果相反

因为 op.is_show_xxxop.is_xxx 数量相当,而且不多。原回答(见下面)通过数组索引的方式来关联他们。然后在一次遍历(reduce 部分)中同时进行过滤(三元运算)和布尔计算(合并 r 值)。因为是 and 运算,所以初始值给 true。最后将结果取反输出。

原回答这个解法虽然有一定的效率,但不是很容易理解,今天再写个按上面分析分步骤进行的:

function isRemove2(op, o) {
    const result = "lock,sf,bz".split(",")
        .map(suffix => [`is_show_${suffix}`, `is_${suffix}`])
        .map(pair => ({ flag: op[pair[0]], value: o[pair[1]] }))
        // ⇑ 上面三行都只是为了生成 { flag: true, value: true } 这样的关系数据
        // ⇓ 下面这行是按 is_show_xxx 过滤
        .filter(pair => pair.flag)
        // 过滤出来的用 && 组合逻辑
        .reduce((r, pair) => r && pair.value, true);
    return !result;
}

不习惯看这种函数式的处理方式,来个原始一点 for..of 循环的

function isRemove3(op, o) {
    const suffixes = "lock,sf,bz".split(",");
    let result = true;
    for (const suffix of suffixes) {
        if (op[`is_show_${suffix}`]) {
            result &&= o[`is_${suffix}`];
        }
    }
    return !result;
}

这一段中的遍历部分(初始化 resultfor)直接翻译成 reduce 形式就是:

function isRemove4(op, o) {
    const suffixes = "lock,sf,bz".split(",");

    let result = suffixes.reduce(
        (r, suffix) => {
            if (op[`is_show_${suffix}`]) {
                return r && o[`is_${suffix}`];
            } else {
                return r;
            }
        },
        true    // 初始化,相当于 let result = true
    );
    return !result;
}

中间的 if...else...看起来不清爽,处理一下(VS 的重构很方便)

Reflectoring.gif

function isRemove4(op, o) {
    const suffixes = "lock,sf,bz".split(",");

    let result = suffixes.reduce(
        (r, suffix) => op[`is_show_${suffix}`] ? r && o[`is_${suffix}`] : r,
        true    // 初始化,相当于 let result = true
    );
    return !result;
}

原回答

问题中的 isDel 保留,方便做单元测试对比,定义了一个 isRemove

function isRemove(op, o) {
    const flags = [op.is_show_lock, op.is_show_sf, op.is_show_bz];
    const values = [o.is_lock, o.is_sf, o.is_bz];
    return !flags.reduce((r, flag, i) => {
        return flag ? r && values[i] : r;
    }, true);
}

测试用例:

// test cases
(() => {
    const cases = Array.from(Array(8), (_, i) => {
        return [!!(i & 0x01), !!(i & 0x02), !!(i & 0x04)];
    });

    let failed = false;
    const allCases = cases.length * cases.length;
    let passed = 0;
    cases.forEach(flags => {
        const op = {};
        ([op.is_show_lock, op.is_show_sf, op.is_show_bz] = flags);
        cases.forEach(values => {
            const o = {};
            ([o.is_lock, o.is_sf, o.is_bz] = values);
            if (isDel(op, o) === isRemove(op, o)) {
                passed++;
            } else {
                failed = true;
                console.log("[Fail]", op, o);
            }
        });
    });

    if (!failed) {
        console.log(`[All passed]: ${passed}/${allCases}`);
    }
})();

全部通过

[All passed]: 64/64

补充一下:就我看提问者上面的这代码质量,他的实现肯定是有问题的,决对不是真正解决他需求的最优解,感觉提问者可以把你的需求提出来。

const filteredList = Vue.computed(() => list.value.filter(item =>
      Object.keys(item)
        .filter(key =>  key.startsWith('is_') && key !== 'is_show')
          .every(key => !show_gl[key.replace('_', '_show_')] || item[key])));

我是真不知道为什么会给我推这个问题,看了大家答案,你们是不是都被提问题的人带偏了?他这很明显就是一个 VUE 项目,一个应该是 computed 去干的事情,为啥都在用 watchlist 的数据,然后,他这个里面的逻辑,就我个人理解,最核心的就两个点:

  1. 过滤的时候,如果选中了 is_show_xxx 那么 item.is_xxx 就必须为 true
  2. 如果没选中 is_show_xxx,那么 item.is_xxx 不参与是否展示的判断

也说了,有很多这种成对出现的 is_xxxis_show_xxx,所以,简单来说,就两步:

  1. 拿到所有 item 的所有 is_xxx
  2. 判断 is_show_xxx 是否为 false,如果为 false 不参与判断,那么这条 field 直接过,如果为 true,参与判断,再判断一个 item.is_xxx 是否为 true 就可以了,判断的时候,要求所有满足条件的都为 true,所以使用的是 every

然后,还是想多说几句

  1. 代码里面,那个过滤功能,如果只是用来展示,不要修改原始数据, Vue 提供了那么好用的 Computed 不用
  2. 代码能乱成这样,原因肯定是写代码之前就没有想好自己要解决的核心问题所至,建议还是多思考,少动手写,写一行是一行,让每一行代码都有价值
  3. 假如说,最后是需要用到上面的结果做持久化存储的,那就直接从 filteredList 里面拿数据就可以了(只要是这个 list 里面的,is_show 就一定为 true,否则就为 false,提交的时候批量改一下这个值就可以了)

似乎是要一一对应的意思
我拿位运算口胡了个

const isDel = (op, o) => {
    return isDel.state(op, "is_show_") === isDel.state(o, "is_")
}
isDel.state = (x, prefix) => {
     let s = 0;
     [ "lock", "sf", "bz" ].forEach((k, i) => {
         if (x[prefix + k]) s = s | 1 << i
     })
     return s
}

似乎是逻辑蕴含的意思,我再口胡一个

const idDel = (o1, o2) => {
    const p1 = "is_show_", p2 = "is_" // 前缀
    return [ "gl1", "gl2", "gl3" ].every(f => ! o1[p1 + f] || o2[p2 + f]))
}

// 用的时候这样

(show_gl) => {
    for (let i = 0; i < list.value.length; i++) {
        list.value[i].is_show = isDel(show_gl, list.value[i])
    }
}

思路:找到list和showObject的字段共同点,便于进行判断,这里共同点就是他们结尾的字符串一样(_gl1),这个你可以根据你的业务场景修改,然后就是你这里的筛选是取并集的,所以在第二层判断那里我把show用数组存起来了,在外层用reduce计算一次并集,把最后的计算结果赋值就可以了,还有一点就是,你这里如果没有筛选,默认就是全显示的,所有reduce的默认值给的是true

watch(show_gl, () => {
    for (let i = 0; i < list.value.length; i++) {
        const listKeys = Object.keys(list.value[i])
        const is_show = []
        for(let key in show_gl) {
            if (show_gl.hasOwnProperty(key) && show_gl[key]) {
                // key.slice(-4) -> _gl1
                const current = listKeys.find(v => v.endsWith(key.slice(-4)))
                is_show.push(list.value[i][current] ?? false)
             }
        }

        list.value[i].is_show = is_show.reduce((a, b) => a && b, true)
    }
});

const isRemove = (op, o) => (op.is_show_lock && !o.is_lock) || (op.is_show_sf && !o.is_sf) || (op.is_show_bz && !o.is_bz)
之前的回答有误,没有验证,凭感觉写的,这次应该没问题了。借用了@边城 的测试。
主要就是运用布尔代数简化表达式,提取了true的情况,列出所有true的布尔逻辑然后根据法则做运算即可
布尔代数运算律

watch(show_gl, () => {

  const transformParams = (key) => {
    const map = {
      is_show_gl1: 'is_gl1',
      is_show_gl2: 'is_gl2',
      is_show_gl3: 'is_gl3',
    }
    return map[key]
  }
  const activeKey = Object.keys(show_gl).filter(key => show_gl[key]).map(transformParams)
  list.value.map(item => {
    item.is_show = activeKey.map(key => item[key]).every(Boolean)
  })

});

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏