mofang-mopai-utils
Version:
Use it in Mopai system
179 lines (169 loc) • 6.85 kB
text/typescript
/**
* A class for performing mathematical calculations on a given formula.
* 魔方魔派前端计算器
*/
class MofangMopaiCalculator {
_formula: string
/**
* Constructs a new instance of MfCalculator.
*
* @param {string} formula - The mathematical formula to be calculated.
* @throws Will throw an error if the formula is not provided or is not a string.
*/
constructor(formula: string){
if(!formula) throw new Error('[传参错误] - 传入公式不能为空')
if(typeof formula !== 'string') throw new Error('[传参错误] - 传入公式必须是字符串格式')
this._formula = formula.replace(/\s*/g, '')
}
/**
* Calculates the final result of the mathematical formula.
*
* @returns {number} The final result of the calculation.
*/
get(): number {
const res = this._splitFormula(this._formula)
return Number(res || 0)
}
/**
* Splits the mathematical formula into smaller parts for individual calculation.
* It continues to do this until there are no more operators left in the formula.
*
* @param {string} formula - The mathematical formula to be split.
* @returns {string} The final result of the calculation.
*
* @example
* _splitFormula('1.23+34.23+33-4-7.8*8.12+103.21') // returns '104.334'
*/
_splitFormula(str: string): string{
// 先乘除 后加减
const methodA = str.match(/\*|\//) // 判断有没有乘除法公式
const methodB = str.match(/\+|\-/) // 判断有没有加减法公式
// 如果未匹配到计算公式,则是计算后的最终结果,直接返回
if(!methodA && !methodB) return str
// 定义计算公式正则表达式
let reg: RegExp
if(methodA){
reg = this._getRegExp(methodA[0])
} else if(methodB){
reg = this._getRegExp(methodB[0])
} else {
throw new Error('[计算错误] - 未能成功获得计算公式正则表达式')
}
// 匹配字符串中的计算公式
const arr: string[] | null = str.match(reg)
const formula: string = arr?.[0] || ''
if(!formula) throw new Error('[计算错误] - 未匹配到计算公式')
// 对公式进行计算
const calculationResult: number = this._startCalc(formula)
// 把相应的计算公式替换成计算后的结果值
const replaceFormula = str.replace(formula, calculationResult.toString())
// 继续进行计算 直到字符串中没有计算公式为止
return this._splitFormula(replaceFormula)
}
/**
* Gets a regular expression based on the given calculation method.
*
* @param {string} method - The calculation method.
* @returns {RegExp} The regular expression.
*/
_getRegExp(method: string): RegExp{
return new RegExp(`\\d+(\\.\\d+)?\\${method}\\d+(\\.\\d+)?`)
}
/**
* Starts the calculation process for a given formula.
*
* @param {string} formula - The mathematical formula to be calculated.
* @returns {number} The result of the calculation.
*
* @example
* _startCalc('1.23+34.23') // returns 35.46
* _startCalc('34.23-33') // returns 1.23
* _startCalc('33*7.8') // returns 257.4
* _startCalc('103.21/8.12') // returns 12.699999999999998
*/
_startCalc(formula: string): number{
if(!formula) throw new Error('[计算错误] - 未找到需要计算的公式')
const reg = /\+|\-|\*|\//g
const method = formula.match(reg)?.[0] // 获取计算符
if(!method) throw new Error('[计算错误] - 计算方法错误')
const numArray = formula.split(reg)
return this._execMath(numArray[0], numArray[1], method)
}
/**
* Executes a mathematical operation on two numbers.
*
* @param {number | string} [a=0] - The first number.
* @param {number | string} [b=0] - The second number.
* @param {string} method - The mathematical operation to be performed.
* @returns {number} The result of the mathematical operation.
* @throws Will throw an error if the method is not one of the following: '+', '-', '*', '/'.
*
* @example
* _execMath(1.23, 34.23, '+') // returns 35.46
* _execMath(34.23, 33, '-') // returns 1.23
* _execMath(33, 7.8, '*') // returns 257.4
* _execMath(103.21, 8.12, '/') // returns 12.699999999999998
*/
_execMath(a: number | string = 0, b: number | string = 0, method: string): number{
// 获取小数点后位数
const decimalLength = Math.max(this._getDecimalLength(a), this._getDecimalLength(b))
const x = this._transformToInteger(a, decimalLength) // 补位后的a值
const y = this._transformToInteger(b, decimalLength) // 补位后的b值
const z = Number(('1').padEnd(decimalLength + 1, '0')) // 补位数
switch (method) {
case '+': {
return (x + y) / z
}
case '-': {
return (x - y) / z
}
case '*': {
return x * y / Math.pow(z, 2)
}
case '/': {
return x / y
}
default: {
return 0
}
}
}
/**
* Gets the decimal length of a number.
*
* @param {number} num - The number to get the decimal length from.
* @returns {number} The decimal length of the number.
*
* @example
* _getDecimalLength(1.23) // returns 2
* _getDecimalLength(34.23) // returns 2
* _getDecimalLength(33) // returns 0
*/
_getDecimalLength(num: string | number): number{
const n = num.toString()
const arr = n.split(/\./)
return arr[1]?.length || 0
}
/**
* Transforms a decimal number into an integer by padding the decimal part with zeros.
* !因为直接进位计算还是会出现小数点后无穷位的问题,所以直接用字符串进行格式化
*
* @param {number} num - The decimal number to be transformed.
* @param {number} offset - The number of decimal places to pad.
* @returns {number} The transformed integer.
*
* @example
* _transformToInteger(1.23, 2) // returns 123
* _transformToInteger(34.23, 3) // returns 34230
* _transformToInteger(33, 1) // returns 330
*/
_transformToInteger(num: string | number, offset: number): number{
if(offset === 0) return Number(num)
const n = num.toString()
const arr = n.split(/\./)
const dec = arr[1]?.toString() || '0'
arr[1] = dec.padEnd(offset, '0')
return Number(arr.join(''))
}
}
export default MofangMopaiCalculator