微信小程序实现带参数的字符串表达式判断真值

12 篇文章 0 订阅
1 篇文章 0 订阅
先看效果图

在这里插入图片描述


在这之前有大佬实现了微信小程序中的eval,亲测过,可行,但体量有点大,决定自己写个简单的eval,不求实现复杂逻辑,够用就行。

一、背景

应公司需求,计算表达式的true,false值,如
"STATUS===10||STATUS===20"

计算之前需将其中的STATUS变量转变为实际的值如:
"10===10||10===20"

为了更好辨认变量,将变量用特殊字符串标记起来,如:[%=STATUS%]
将原表达式定为"[%=STATUS%]===10||[%=STATUS%]===20"

二、实现逻辑

将给定表达式"[%=STATUS%]===10||[%=STATUS%]===20"分解成数组,同时将标记的变量STATUS赋值并去掉标记字符串,得到的字符串如下:
(假设实际STATUS==='10'
['10', '===', '10', '||', '10', '===', '20']
根据数组中的||&&拆分成两个简单的数组['10', '===', '10']'10', '===', '20']
分别硬判断得到两个boolean值,再将两个boolean值做||&&操作,得到最终的boolean。
当然这里是两个小表达式组成的情况,如果是三个或以上的情况,就要用到循环算法来解决,这里我采用递归算法。

三、贴上代码

封装类utils.js

const sfEval = (model, str, property = null) => {
  let cs = str.replace(/\s*/g, '').replace(/\[%=/g, '').replace(/%\]/g, '').split(/(&&|\|\||>|<|===|!==|>=|<=|\(|\))/)
  for (const cIdx in cs) {
    if (property && model[cs[cIdx]] !== undefined) {
      // 处理0.aaa的问题
      // 处理以0开头的字符串
      // parseFloat
      // parseFloat('') NaN
      // parseFloat(null) NaN
      // parseFloat(true) NaN
      // parseFloat(false) NaN
      // Number('0.aaa') NAN
      // 不是''不是null不是true不是false
      // 是false // 后面即使是true也不走
      // 是 0.aaa true // 后面是false
      // 对象和数组都是parseFloat之后都是NaN
      if (model[cs[cIdx]]) {
        if (!isNaN(parseFloat(model[cs[cIdx]][property])) && !isNaN(Number(model[cs[cIdx]][property]))) {
          // 之后一个判断处理字符串'0000'的情况
          // 如何处理
          if ((model[cs[cIdx]][property] + '').charAt() === '0' && (model[cs[cIdx]][property] + '').length > 1) {
            cs[cIdx] = "'" + model[cs[cIdx]][property] + "'"
          } else {
            cs[cIdx] = parseFloat(model[cs[cIdx]][property])
          }
        } else {
          // 处理下拉框的清除问题 null以及其他null的问题
          if (model[cs[cIdx]][property] === null) {
            model[cs[cIdx]][property] = ''
          }
          cs[cIdx] = "'" + model[cs[cIdx]][property] + "'"
        }
      }
    } else if (model[cs[cIdx]] !== undefined) {
      // 处理0.aaa的问题
      // 处理以0开头的字符串
      // parseFloat
      // parseFloat('') NaN
      // parseFloat(null) NaN
      // parseFloat(true) NaN
      // parseFloat(false) NaN
      // Number('0.aaa') NAN
      // 不是''不是null不是true不是false
      // 是false // 后面即使是true也不走
      // 是 0.aaa true // 后面是false
      // 对象和数组都是parseFloat之后都是NaN
      if (!isNaN(parseFloat(model[cs[cIdx]])) && !isNaN(Number(model[cs[cIdx]]))) {
        // 之后一个判断处理字符串'0000'的情况
        // 如何处理
        if ((model[cs[cIdx]] + '').charAt() === '0' && (model[cs[cIdx]] + '').length > 1) {
          cs[cIdx] = "'" + model[cs[cIdx]] + "'"
        } else {
          cs[cIdx] = parseFloat(model[cs[cIdx]])
        }
      } else {
        // 处理下拉框的清除问题 null以及其他null的问题
        if (model[cs[cIdx]] === null) {
          model[cs[cIdx]] = ''
        }
        cs[cIdx] = "'" + model[cs[cIdx]] + "'"
      }
    } else if ((cs[cIdx].indexOf("'") < 0 || (!isNaN(parseFloat(cs[cIdx].replace(/'/g, ''))) && !isNaN(Number(cs[cIdx].replace(/'/g, ''))))) && !cs[cIdx].match(/^(&&|\|\||>|<|===|!==|>=|<=|\(|\))$/)) {
      // convert to string fix bug for model is null with FIELD = '0', FIELD is undefined
      if (!isNaN(parseFloat(cs[cIdx].replace(/'/g, ''))) && !isNaN(Number(cs[cIdx].replace(/'/g, '')))) {
        if ((cs[cIdx] + '').charAt() === '0' && (cs[cIdx] + '').length > 1) {
          cs[cIdx] = "'" + cs[cIdx] + "'"
        } else {
          cs[cIdx] = parseFloat(cs[cIdx].replace(/'/g, ''))
        }
      } else {
        cs[cIdx] = "'" + cs[cIdx] + "'"
      }
    }
  }

  let csTemp = JSON.parse(JSON.stringify(cs))

  return true && fn(csTemp)
}

const fn = (csTemp) => {
  let resultBool = false
  let arr1 = []
  let arr2 = []
  if (csTemp.length < 4) {
    resultBool = calcFuc(csTemp)
    return resultBool
  } else {
    for (let i in csTemp) {
      if (csTemp[i] == '&&' || csTemp[i] == '||') {
        arr1 = csTemp.slice(0, i)
        arr2 = csTemp.slice(Number(i) + 1, Number(i) + 4)
        if (csTemp[i] == '&&') {
          resultBool = calcFuc(arr1) && calcFuc(arr2)
        } else {
          resultBool = calcFuc(arr1) || calcFuc(arr2)
        }
        csTemp.splice(0, Number(i) + 4, resultBool)
        return fn(csTemp)
        break
      }
    }
  }
}

const calcFuc = (arr) => {
  for (let i = 0; i < arr.length; i++) {
    switch (arr[i]) {
      case '&&':
        return arr[i - 1] && arr[i + 1]
      case '||':
        return arr[i - 1] || arr[i + 1]
      case '>':
        return arr[i - 1] > arr[i + 1]
      case '<':
        return arr[i - 1] < arr[i + 1]
      case '===':
        return arr[i - 1] === arr[i + 1]
      case '!==':
        return arr[i - 1] !== arr[i + 1]
      case '>=':
        return arr[i - 1] >= arr[i + 1]
      case '<=':
        return arr[i - 1] <= arr[i + 1]
      case true:
        return true
      default:
        break
    }
  }
  return false
}

module.exports = {
  sfEval
}
四、调用方法

先import封装方法,然后调用

let model = {
      USER: 'a',
      STATUS: 0,
      ROLE: 2
    }
let str = "[%=STATUS%]==='0'&&[%=ROLE%]==='2'&&[%=USER%]==='a'"
console.log('sfEval', sfEval(model, str))
五、运行结果

在这里插入图片描述

六、注意事项

本篇文章实现逻辑中包含的eval可仅跑通类似以下形式的字符串:

"2===2&&3!==4"
"2===2&&3!==4&&'a'!=='b'"
"2===2&&3!==4&&'a'!=='b'||'a'!=='b'"
"2===2||3!==4&&'a'!=='b'||'a'!=='b'"

不支持括号,按照从左到右的顺序计算。

七、结束语

到此就结束了,希望能帮到各位,记得拿走时请扣6
有问题在评论区留言

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,实现真值表达式树需要用到二叉树和递归算法,以下是一个简单的 Python 实现: 首先,我们定义一个二叉树节点类,包含了节点的值、左子树和右子树: ```python class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None ``` 然后,我们定义一个函数 `build_tree(expr)` 来构建真值表达式树,其中 `expr` 是一个字符串,表示一个真值表达式: ```python def build_tree(expr): if len(expr) == 1: return TreeNode(expr[0]) else: root = TreeNode(expr[-1]) i = len(expr) - 2 while i >= 0 and expr[i] not in ['&', '|', '!']: i -= 1 root.left = build_tree(expr[:i+1]) root.right = build_tree(expr[i+1:-1]) return root ``` 这个函数的实现利用了递归的思想,对于一个长度大于 1 的表达式,我们可以找到最后一位符号作为根节点,然后递归构建左子树和右子树。 最后,我们定义一个函数 `evaluate(root, vars)` 来计算真值表达式树的值,其中 `root` 是真值表达式树的根节点,`vars` 是一个字典,表示变量名和对应的真值: ```python def evaluate(root, vars): if root.val == '!': return not evaluate(root.right, vars) elif root.val == '&': return evaluate(root.left, vars) and evaluate(root.right, vars) elif root.val == '|': return evaluate(root.left, vars) or evaluate(root.right, vars) else: return vars[root.val] ``` 这个函数的实现也利用了递归的思想,对于一个非操作符的节点,我们根据节点的值和变量真值字典来计算节点的值;对于一个非操作符的节点,我们递归计算其右子树的值,并对其取反;对于一个与或操作符的节点,我们递归计算其左右子树的值,并进行相应的逻辑运算。 完整的实现代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值