Skip to content

Instantly share code, notes, and snippets.

@rfprod
Created December 10, 2017 20:25
Show Gist options
  • Save rfprod/9a785381d3f38b38dc1dca94e460d4f5 to your computer and use it in GitHub Desktop.
Save rfprod/9a785381d3f38b38dc1dca94e460d4f5 to your computer and use it in GitHub Desktop.
Calculator script
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

Calculator script

Performs mathematical expression evaluation, takes string input, ignores spaces, returns a number, e.g.

  • calc('-123') = -123
  • calc('-1 +1') = 0
  • calc('2 /2+3 * 4.75- -6') = 21.25
  • calc('2 / (2 + 3) * 4.33 - -6') = 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)') = 1

A script by V.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment