Nation
Nation

Reputation: 506

Math operations from string using Javascript

I am trying to find a simple way to perform a set of javascript math operations without using eval() function. Example: 1+2x3x400+32/2+3 and it must follow the PEMDAS math principle. This is what I have, but it doesn't work exactly it should.

function mdas(equation) {

    let operations = ["*", "/", "+", "-"];

    for (let outerCount = 0; outerCount < operations.length; outerCount++) {
        for (let innerCount = 0; innerCount < equation.length; ) {
            if (equation[innerCount] == operations[outerCount]) {

                let operationResult = runOperation(equation[innerCount - 1], operations[outerCount], equation[innerCount + 1]);

                var leftSideOfEquation = equation.substr(0, equation.indexOf(innerCount - 1));
                var rightSideOfEquation = equation.substr(equation.indexOf(innerCount), equation.length);
                
                var rightSideOfEquation = rightSideOfEquation.replace(rightSideOfEquation[0],String(operationResult));
                equation = leftSideOfEquation + rightSideOfEquation;
                innerCount = 0;
            }
            else {
                innerCount++;
            }
        }
    }
    return "Here is it: " + equation; //result of the equation
}

Upvotes: 1

Views: 1856

Answers (2)

Roko C. Buljan
Roko C. Buljan

Reputation: 206688

If you don't want to use a complete library like mathjs - and you don't want to tackle creating your own script which would involve: lexical analysis, tokenization, syntax analysis, recursive tree parsing, compiling and output...
the simplest banal suggestion: Function

const calc = s => Function(`return(${s})`)();

console.log( calc("1+2*3*400+32/2+3") ); // 2420
console.log( calc("-3*-2") );            // 6
console.log( calc("-3 * + 1") );         // -3
console.log( calc("-3 + -1") );          // -4
console.log( calc("2 * (3 + 1)") );      // 8

My take at a custom MDAS

  • Here I created a Regex to retrieve operands and operators, accounting for negative values: /(-?[\d.]+)([*\/+-])?/g.
  • Firstly we need to remove any whitespace from our string using str.replace(/ /g , "")
  • Using JavaScript's String.prototype.matchAll() we can get a 2D array with all the matches as [[fullMatch, operand, operator], [.. ] we can than further flatten it using Array.prototype.flat()
  • Having that flattened array, we can now filter it using Array.prototype.filter() to remove the fullMatch -es returned by the regular expression and remove the last undefined value.
  • Define a calc Object with the needed operation functions
  • Iterate over the MDAS groups */ and than +- as regular expressions /\/*/ and /+-/
  • Consume finally the array of matches until only one array key is left

let str = "-1+2 * 3*+400+-32 /2+3.1";   // 2386.1
str = str.replace(/ +/g, "");           // Remove all spaces!

// Get operands and operators as array.
// Remove full matches and undefined values.
const m = [...str.matchAll(/(-?[\d.]+)([*\/+-])?/g)].flat().filter((x, i) => x && i % 3);

const calc = {
  "*": (a, b) => a * b,
  "/": (a, b) => a / b,
  "+": (a, b) => a + b,
  "-": (a, b) => a - b,
};

// Iterate by MDAS groups order (first */ and than +-)
[/[*\/]/, /[+-]/].forEach(expr => {
  for (let i = 0; i < m.length; i += 2) {
    let [a, x, b] = [m[i], m[i + 1], m[i + 2]];
    x = expr.exec(x);
    if (!x) continue;
    m[i] = calc[x.input](parseFloat(a), parseFloat(b)); // calculate and insert
    m.splice(i + 1, 2);                                 // remove operator and operand
    i -= 2;                                             // rewind loop
  }
});

// Get the last standing result
console.log(m[0]);      // 2386.1

Upvotes: 4

Chris Strickland
Chris Strickland

Reputation: 3490

It's a little hacky, but you can try something like this:

var eqs = [
'1+2*3*4+1+1+3',
'1+2*3*400+32/2+3',
'-5+2',
'3*-2',
];

for(var eq in eqs) { console.log(mdas(eqs[eq])); }

function mdas(equation) {

console.log(equation);

var failsafe = 100;
var num = '(((?<=[*+-])-|^-)?[0-9.]+)';
var reg = new RegExp(num + '([*/])' + num);

while(m = reg.exec(equation)) {
    var n = (m[3] == "*") ? m[1]*m[4] : m[1]/m[4];
    equation = equation.replace(m[0], n);
    if(failsafe--<0) { return 'failsafe'; } 
}

var reg = new RegExp(num + '([+-])' + num);
while(m = reg.exec(equation)) {
    var n = (m[3] == "+") ? 1*m[1] + 1*m[4] : m[1]-m[4];
    equation = equation.replace(m[0], n);
    if(failsafe--<0) { return 'failsafe'; }
}

return equation;

}

Upvotes: 1

Related Questions