|
function calc(expression) { |
|
let numbers = []; |
|
let operators = []; |
|
|
|
let output; |
|
let currentValue = expression.replace(/\s/g, ''); |
|
|
|
const add = (a,b) => parseFloat(a) + parseFloat(b); |
|
const subtract = (a,b) => parseFloat(a) - parseFloat(b); |
|
const multiply = (a,b) => parseFloat(a) * parseFloat(b); |
|
const divide = (a,b) => parseFloat(a) / parseFloat(b); |
|
|
|
const calculate = (expr) => { |
|
let tmp = ""; |
|
for (let i = 0, max = expr.length; i < max; i++){ |
|
const char = expr.charAt(i); |
|
let negative = false; |
|
if (i) { |
|
negative = /[-+%/*]/.test(expr.charAt(i-1)) && char === '-'; |
|
} else { |
|
negative = char === '-'; |
|
} |
|
if (!isNaN(char) || char === '.' || negative){ |
|
tmp = tmp.concat(char); |
|
} else { |
|
if (char === "%"){ |
|
numbers.push(tmp.concat(char)); |
|
tmp = ""; |
|
}else{ |
|
if (tmp !== ""){ |
|
numbers.push(tmp); |
|
} |
|
tmp = ""; |
|
operators.push(char); |
|
} |
|
} |
|
if (i == max - 1){ |
|
numbers.push(tmp); |
|
tmp = ""; |
|
} |
|
} |
|
|
|
numbers = numbers.map((item) => item.replace(/\-\-/g, '')); // remove excessive minuses |
|
|
|
console.log("nmbrs: ", numbers);// debug numbers array |
|
console.log("oprtrs: ", operators);// debug operators array |
|
|
|
let multiplyOrDivide = []; |
|
let calculationMode = "";// possible values: sequential, nonsequential |
|
// reorder array if string contains * or / |
|
let doIt = "false"; |
|
// check if array has to be rearranged - DEBUG |
|
for (let l = 0, max = operators.length - 1; l < max; l++){ |
|
if ((operators[0].indexOf("*") > -1) || (operators[0].indexOf("/") > -1)){ |
|
if (operators[l].indexOf("*") > -1){ |
|
if(operators[l+1].indexOf("+") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
if (operators[l].indexOf("*") > -1){ |
|
if(operators[l+1].indexOf("-") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
if (operators[l].indexOf("/") > -1){ |
|
if (operators[l+1].indexOf("+") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
if (operators[l].indexOf("/") > -1){ |
|
if(operators[l+1].indexOf("-") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
} |
|
if ((operators[0].indexOf("+") > -1) || (operators[0].indexOf("-") > -1)){ |
|
if (operators[l+1].indexOf("*") > -1){ |
|
if (operators[l].indexOf("+") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
if (operators[l+1].indexOf("*") > -1){ |
|
if(operators[l].indexOf("-") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
if (operators[l+1].indexOf("/") > -1){ |
|
if(operators[l].indexOf("+") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
if (operators[l+1].indexOf("/") > -1){ |
|
if(operators[l].indexOf("-") > -1) { |
|
doIt = "true"; |
|
} |
|
} |
|
} |
|
} |
|
// rearrange array if needed |
|
if (doIt != "false") putChainToOrder(numbers,operators); |
|
|
|
// actual calculation loop |
|
let result = numbers[0]; |
|
console.log("result[0]:", result); |
|
for (let j = 0, max = operators.length; j < max; j++){ |
|
console.log("numbers", numbers, "| operators:", operators); |
|
if (String(numbers[j+1]).indexOf("%") > -1){ |
|
const number = numbers[j+1].substring(0,numbers[j+1].length-1); |
|
console.log("% number:", number); |
|
numbers[j+1] = numbers[j] * number / 100; |
|
} |
|
if (operators[j] == "+"){ |
|
result = add(result,parseFloat(numbers[j+1])); |
|
} |
|
if (operators[j] == "-"){ |
|
result = subtract(result,parseFloat(numbers[j+1])); |
|
} |
|
if (operators[j] == "*"){ |
|
result = multiply(result,parseFloat(numbers[j+1])); |
|
} |
|
if (operators[j] == "/"){ |
|
result = divide(result,parseFloat(numbers[j+1])); |
|
} |
|
} |
|
|
|
console.log("result:", result); |
|
if (isNaN(result)) result = 'undefined'; |
|
|
|
return result; |
|
} |
|
|
|
const putChainToOrder = (numbers, operators) => { |
|
let inputChain = []; |
|
let multiplyOrDivide = []; |
|
// form input chain array |
|
for (let c = 0, max = numbers.length; c < max; c++){ |
|
inputChain.push(numbers[c]); |
|
if (c >= 0) { |
|
inputChain.push(operators[c]); |
|
} |
|
} |
|
console.log("inputChain:", inputChain);// debug input chain |
|
// detect indexes of multipliers or dividers: multiplyOrDivide |
|
for (let d = 0, max = operators.length; d < max; d++){ |
|
if ((operators[d] == "*") || (operators[d] == "/")){ |
|
multiplyOrDivide.push(d); |
|
} |
|
} |
|
console.log("multiplyOrDivide:", multiplyOrDivide);// debug multiply or divide operators array |
|
// check if multiplication and/or division operations are sequential or not, if several |
|
// result is calculationMode value: sequential or nonsequential |
|
if (multiplyOrDivide.length >= 1){ |
|
if (multiplyOrDivide.length > 1){ |
|
for (let m = 1, max = multiplyOrDivide.length - 1; m <= max; m++){ |
|
calculationMode = "sequential"; |
|
const difference = multiplyOrDivide[m] - multiplyOrDivide[m-1]; |
|
if (difference > 1){ |
|
calculationMode = "nonsequential"; |
|
} |
|
} |
|
}else{ |
|
calculationMode = "sequential"; |
|
} |
|
console.log('calc mode:', calculationMode); |
|
} |
|
// if calculation mode is sequential, reorder array |
|
if ((calculationMode == "sequential") || (calculationMode == "nonsequential")){ |
|
let newNumbers = numbers; |
|
let newOperators = operators; |
|
for (let h = 0, max = multiplyOrDivide.length; h < max; h++){ |
|
const operatorIndex = multiplyOrDivide[h] - h; |
|
console.log("operatorIndex:", operatorIndex); |
|
let firstNumber = 0; |
|
let secondNumber = 0; |
|
if (operatorIndex > numbers.length-1){ |
|
firstNumber = parseFloat(numbers[operatorIndex-1]); |
|
if (String(numbers[operatorIndex]).indexOf("%") > -1){ |
|
const number = numbers[operatorIndex].substring(0,numbers[operatorIndex].length-1); |
|
console.log("% number:", number); |
|
numbers[operatorIndex] = numbers[operatorIndex-1]*number/100; |
|
} |
|
secondNumber = parseFloat(numbers[operatorIndex]); |
|
}else{ |
|
firstNumber = parseFloat(numbers[operatorIndex]); |
|
if (String(numbers[operatorIndex+1]).indexOf("%") > -1){ |
|
const number = numbers[operatorIndex+1].substring(0,numbers[operatorIndex+1].length-1); |
|
console.log("% number:", number); |
|
numbers[operatorIndex+1] = numbers[operatorIndex]*number/100; |
|
} |
|
secondNumber = parseFloat(numbers[operatorIndex+1]); |
|
} |
|
if (operators[operatorIndex] == "*"){ |
|
const newValue = firstNumber * secondNumber; |
|
newNumbers.splice(operatorIndex+1,1,newValue); |
|
} |
|
if (operators[operatorIndex] == "/"){ |
|
const newValue = firstNumber / secondNumber; |
|
newNumbers.splice(operatorIndex+1,1,newValue); |
|
} |
|
newOperators.splice(operatorIndex,1); |
|
newNumbers.splice(operatorIndex,1); |
|
console.log("newOperators:", newOperators); |
|
console.log("newNumbers:", newNumbers); |
|
} |
|
numbers = newNumbers; |
|
operators = newOperators; |
|
console.log("operators:", operators); |
|
console.log("numbers:", numbers); |
|
} |
|
} |
|
|
|
if (/^(\-)?\d+$/.test(currentValue)) { |
|
console.log('result:', expression); |
|
output = (/\./.test(expression)) ? parseFloat(expression) : parseInt(expression, 10); |
|
} else { |
|
let match = currentValue.match(/\([^)(]+\)/); |
|
while (match) { |
|
console.log("match", match); |
|
const tempOut = calculate(match[0].replace(/[)(]/g, '')); |
|
currentValue = currentValue.replace(match[0], tempOut); |
|
match = currentValue.match(/\([^)(]+\)/); |
|
numbers = []; |
|
operators = []; |
|
} |
|
output = calculate(currentValue); |
|
output = (/\./.test(output)) ? parseFloat(output) : parseInt(output, 10); |
|
} |
|
console.log('output', output); |
|
return output; |
|
}; |
|
|
|
/* |
|
* TEST |
|
*/ |
|
// calc('-123'); // result: -123 |
|
// calc('-1 +1'); // result: 0 |
|
// calc('2 /2+3 * 4.75- -6'); // result: 21.25 |
|
// calc('2 / (2 + 3) * 4.33 - -6'); // result: 7.732 |
|
// calc('(123.45*(678.90/(-2.5+11.5)-(((80-(19)))*33.25))/20)-(123.45*(678.90/(-2.5+11.5)-(((80-(19)))*33.25))/20)+(13-2)/-(-11)'); // result: 1 |