Evaluate mathematical expression

Instructions

Given a mathematical expression as a string you must return the result as a number.

Numbers

Number may be both whole numbers and/or decimal numbers. The same goes for the returned result.

Operators

You need to support the following mathematical operators:

  • Multiplication *
  • Division / (as floating point division)
  • Addition +
  • Subtraction -

Operators are always evaluated from left-to-right, and * and / must be evaluated before + and -.

Parentheses

You need to support multiple levels of nested parentheses, ex. (2 / (2 + 3.33) * 4) - -6

Whitespace

There may or may not be whitespace between numbers and operators.

An addition to this rule is that the minus sign (-) used for negating numbers and parentheses will never be separated by whitespace. I.e all of the following are valid expressions.

1-1    // 0
1 -1   // 0
1- 1   // 0
1 - 1  // 0
1- -1  // 2
1 - -1 // 2
1--1   // 2

6 + -(4)   // 2
6 + -( -4) // 10

And the following are invalid expressions:

1 - - 1    // Invalid
1- - 1     // Invalid
6 + - (4)  // Invalid
6 + -(- 4) // Invalid

Validation

You do not need to worry about validation - you will only receive valid mathematical expressions following the above rules.

Restricted APIs

NOTE: Both eval and Function are disabled.


JavaScript

function splitSubtractionSequence(expr) {
  const result = [];
  let current = "";
  for (let i = 0; i < expr.length; i++) {
    const char = expr[i];
    if (char === "-") {
      result.push(current);
      current = "";
    }
    current += char;
  }
  result.push(current);
  return result.filter((item) => Boolean(item));
}

function evalExpressionGroup(expr) {
  // Remove all spaces from the expression
  expr = expr.replace(/\s+/g, "");

  // Convert all double negatives to a positive
  expr = expr.replace(/--/g, "+");

  // Handle strings of subtraction without executing them
  let numbers = expr.split(/[^0-9\-\.]/);
  numbers = numbers.map((item) => splitSubtractionSequence(item));
  numbers = numbers.reduce((acc, val) => acc.concat(val), []);

  return expr;
};

function calc(expr) {
  // Capture all parenthesis groups for proper order of operations
  const groups = expr.match(/\(.*\)/g) || [];

  // Evaluate each set of parenthesis
  for (let i = 0; i < groups.length; i++) {
    const parenthesis = groups[i].slice(1, -1);
    const output = evalExpressionGroup(parenthesis);
    expr = expr.replace(groups[i], output);
  }

  // Evaluate and return the simplified expression
  return evalExpressionGroup(expr);
};

Tags

  1. javascript (Private)
  2. math (Private)
  3. restricted (Private)
  4. 2-kyu (Private)
  5. codewars (Private)
  6. answer (Private)