1

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
}
2
  • Can you describe some example cases where your code isn't working as it should? Commented Nov 23, 2020 at 23:16
  • @kylejrp The current example doesn't work and using something as simple as 1+2*3*4+1+1+3 does not work Commented Nov 24, 2020 at 0:37

2 Answers 2

4

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

Sign up to request clarification or add additional context in comments.

7 Comments

@Nation: I can probably handle the negative numbers after operators with a positive lookbehind in the regex, but considering how clean this is I don't know that there's any point. Look good to you?
@ChrisStrickland the regex is pretty simple actually. I'm giving it a try too... for fun... Will update soon
@RokoC.Buljan this is awesome! I really good to understand the science behind how simple math work and how we can use a programming language to achieve its basic calculations.
Both answers are great and showing different ways to be creative in order to solve the problem.
@Nation writing complex code becomes an art. Well spotted.
|
1

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;

}

1 Comment

Comments are not for extended discussion; this conversation has been moved to chat.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.