替代js中计算字符串格式的值

问题描述 投票:0回答:1

最近我的任务是为后端的产品创建一个计算机制。有许多产品在计算方面是独一无二的。由于将有一个用于添加新产品的管理面板,我希望使其尽可能动态,这样我就不必编辑后端来添加简单的公式。在数据库中,在 product 表中,我想指定一列作为 JSON 类型来存储计算的蓝图:

calculation: {
      bottom: {
        length: "%v + 100",
        width: "%v + 100",
        UC: "%v * 12",
      },
      side: {
        length: "%v - 50",
        width: "%v + 4",
        UC: "%v * 11",
      },
    },

我想不出更好的主意来为我的传入变量插入占位符

%v

结构将是相同的。意思是,唯一的动态部分是属性的能力(在示例中是bottomside)。

现在的问题是,有了这个想法,评估蓝图确实很危险 因为我必须使用

.eval()
。例如,我创建了以下代码:

    // Example 
    const products = [
      {
        id: 1,
        name: "something",
        calculation: {
          bottom: {
            length: "%v + 100",
            width: "%v + 100",
            UC: "%v * 12",
          },
          side: {
            length: "%v - 50",
            width: "%v + 4",
            UC: "%v * 11",
          },
        },
      },
    ];
    // calculation function
    function handleCalculation(calcObj, initialValues) {
      const updatedCalcObj = {};
    
      for (let prop in calcObj) {
        updatedCalcObj[prop] = {};
    
        for (let attr in calcObj[prop]) {
          const expression = calcObj[prop][attr].replace(
            "%v",
            initialValues[prop][attr]
          );
          // !!! DANGER IS HERE.
          updatedCalcObj[prop][attr] = eval(expression);
        }
      }
    
      return updatedCalcObj;
    }
    
    // example of body of a request
    const initialValues = {
      bottom: {
        length: 100,
        width: 200,
        UC: 1,
      },
      side: {
        length: 120,
        width: 50,
        UC: 1,
      },
    };
    
    // seeing the result
    console.log(handleCalculation(products[0].calculation, initialValues))

您遇到过类似的情况吗?有更好的方法来解决这个问题吗?

javascript node.js json eval
1个回答
0
投票

利用 JSON 和数学表达式都可以解析为树的事实并定义一个简单的解析器:

// Example 
const products = [
    {
        id: 1,
        name: "something",
        calculation: {
            bottom: {
                length: {add: ["%v", 100]},
                width: {add: ["%v", 100]},
                UC: {mult: ["%v", 12]},
            },
            side: {
                length: {sub: ["%v", 50]},
                width: {add: ["%v", 4]},
                UC: {mult: ["%v", 11]},
            },
            nested_example: {
                // (v + 100) * (v / 2 - 20)
                result: {mult: [
                    {add: ["%v", 100]},
                    {sub: [
                        {div:["%v", 2]}, 
                        20
                    ]},
                ]},
            },
        },
    },
];
const operations = {
    add: (a, b) => a + b,
    sub: (a, b) => a - b,
    mult: (a, b) => a * b,
    div: (a, b) => a / b,
};
function calculate(calcObj, initialValue) {
    if (calcObj === "%v") return initialValue;
    let [operation, [left, right]] = Object.entries(calcObj)[0];
    if (typeof left !== "number") {
        left = calculate(left, initialValue);
    }
    if (typeof right !== "number") {
        right = calculate(right, initialValue);
    }
    return operations[operation](left, right);
}
// calculation function
function handleCalculation(calcObj, initialValues) {
    const updatedCalcObj = {};

    for (let prop in calcObj) {
        updatedCalcObj[prop] = {};

        for (let attr in calcObj[prop]) {
            const calculation = calcObj[prop][attr];
            const value = initialValues[prop][attr];
            updatedCalcObj[prop][attr] = calculate(calculation, value);
        }
    }

    return updatedCalcObj;
}

// example of body of a request
const initialValues = {
    bottom: {
        length: 100,
        width: 200,
        UC: 1,
    },
    side: {
        length: 120,
        width: 50,
        UC: 1,
    },
    nested_example: {
        result: 100,
    },
};

// seeing the result
console.log(handleCalculation(products[0].calculation, initialValues))
© www.soinside.com 2019 - 2024. All rights reserved.