skip to Main Content

Working on a Calculator function. Everything works as it should with one exception.
Any single operation or string of operations with a negative number results in NaN instead of the correct answer.

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Calculator II</title>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js" defer></script>
  </head>
  <body>
    <div class="display"><output id="output">Display</output></div>
    <div id="buttons">
      <div id="row1">
        <button class="oper" id="+">+</button><button class="oper" id="-">-</button><button class="oper" id="*">*</button>
      </div>
      <div id="row2">
        <button class="oper" id="/">/</button><button class="alt" id="back"><-</button><button class="alt" id="clear">C</button>
      </div>
      <div id="row3">
        <button class="nbr" id="1">1</button><button class="nbr" id="2">2</button><button class="nbr" id="3">3</button>
      </div>
      <div id="row4">
        <button class="nbr" id="4">4</button><button class="nbr" id="5">5</button><button class="nbr" id="6">6</button>
      </div>
      <div id="row5">
        <button class="nbr" id="7">7</button><button class="nbr" id="8">8</button><button class="nbr" id="9">9</button>
      </div>
      <div id="row6">
        <button class="nbr" id="0">0</button><button id="decimal">.</button><button class="alt" id="equals">=</button>
      </div>
    </div>
  </body>
</html>

JS

let display = "0";
let result = 0;
let num1 = "";
let operand = "";
let num2 = "";
let outputElement = document.getElementById("output");

//basic math functions
function add(num1, num2) {
  result = display = num1 + num2; // combining result and display to a single line.
  console.log("add", result, "d", display);
  return result;
}

function subtract(num1, num2) {
  result = display = num1 - num2;
  console.log("sub", result, "d", display);
  return result;
}

function multiply(num1, num2) {
  result = display = num1 * num2;
  console.log("mult", result, "d", display);
  return result;
}

function divide(num1, num2) {
  result =
    num2 === 0
      ? (console.log("divide by zero? Hoser!"),
        alert("cannot divide by zero"),
        0)
      : (display = num1 / num2);
}

function clear() {
  result = 0;
  display = "0";
  num1 = "";
  operand = "";
  num2 = "";
  console.log("clear", num1, operand, num2, "d", display);
}

function performCalculation(){
  switch (operand) {
    case "+":
      console.log(operand);
      add(parseFloat(num1), parseFloat(num2));
      break;
    case "-":
      console.log(operand);
      subtract(parseFloat(num1), parseFloat(num2));
      break;
    case "*":
      console.log(operand);
      multiply(parseFloat(num1), parseFloat(num2));
      break;
    case "/":
      console.log(operand);
      divide(parseFloat(num1), parseFloat(num2));
      break;
    default:
      console.log("switch", "Houston we have a problem!");
  }
}

function equals() {
  console.log("equals");
  const calculationString = display.replace(/[^0-9+-*/.]/g, "");
  console.log('cs', calculationString);
  const calculations = calculationString.split(/([-+*/])/);
  console.log('c', calculations);

  result = parseFloat(calculations[0]);
  display = result.toString();

  for (let i = 1; i < calculations.length; i += 2) {
    operand = calculations[i];
    num1 = display;
    num2 = calculations[i + 1];

    performCalculation();

    display = result.toString();
  }
outputElement.textContext = display;
}

let buttons = document.getElementById("buttons");
buttons.addEventListener("click", clickHandler);

function clickHandler(e) {
  if (display === "0") {
    display = "";
  }
  let clicked = e.target;
  if (clicked.classList.contains("nbr")) {
    let number = clicked.id;
    // if (display === "0") {
    //   display = "";
    // }
    if (operand === "") {
      if (!num1.includes(".") || num1.split(".")[1].length < 3) {
        display += number;
        num1 += number;
      }
    } else {
      if (!num2.includes(".") || num2.split(".")[1].length < 3) {
        display += number;
        num2 += number;
      }
    }
  } else if (clicked.classList.contains("oper")) {
    display += clicked.id;
    if (clicked.id === "-" && operand === "") {
      if (num1 === "") {
        num1 += "-";
      } else {
        num2 += "-";
      }
    } else {
      operand = clicked.id;
            console.log("CH Oper", operand);
    }
  } else if (clicked.id === "equals") {
    console.log("=");
    equals();
    display = parseFloat(display).toFixed(3).toString();
    display = display.replace(/.?0*$/, "");
  } else if (clicked.id === "clear") {
    clear();
  } else if (clicked.id === "back") {
    display = display.slice(0, -1);
    if (operand === "") {
      num1 = num1.slice(0, -1);
    } else {
      num2 = num2.slice(0, -1);
    }
  } else if (clicked.id === "decimal") {
    display += ".";
    if (operand === "") {
      if (!num1.includes(".")) {

        num1 += ".";
      }
    } else {
      if (!num2.includes(".")) {
        num2 += ".";
      }
    }
  }

  console.log("clicked", clicked, "d", display);
  console.log(num1, operand, num2, "d", display);
  outputElement.textContent = display;
}

I believe the problem is with ‘calculation’ reading the array that is separating in nums and operands.
I have tried a variety of tweaks to calculationString. The following was tried from the bottom up:

const calculationString = display.replace(/[^0-9+-*/.]/g, "");
const calculations = calculationString.split(/([+-*/])/).filter(Boolean);
const calculations = calculationString.split(/([-+*/])/).filter(Boolean);
const calculations = calculationString.split(/(?<!d)-|+|*|//);
const calculations = calculationString.split(/([-+*/])s*/);
const calculations = calculationString.split(/(-?[-+*/])/);

I believe those are called regular expressions and I really do not understand them very well.

This is the last hurdle for me to wrap up this project short of CSS beautification. Help is much appreciated.

2

Answers


  1. It’s a complicated process to calculate an operation based on a string input, you can check some of the answers from here. Now a quick fix for your calculator is separating * and / first then doing + and -.

    (Note: this doesn’t work for bigger numbers since I’m doing the calculation.length > 2, there’s a lot of checks to do there if it starts with a "-" and doesn’t have another addition or subtraction afterwards)

      const firstCalculations = calculationString.split(/([*/])/);
      const calculations = firstCalculations.map((calculation) => {
        if (calculation.length > 2) {
          const secondCalculations = calculation.split(/([+-])/);
          return secondCalculations;    
        }
        return calculation;
      });
    

    Then add a fix for the first number just in case

      if (calculations[0] === "") {
        result = 0;
      } else {
        result = parseFloat(calculations[0]);
      }
    

    You can expand your calculator here:

    let display = "0";
    let result = 0;
    let num1 = "";
    let operand = "";
    let num2 = "";
    let outputElement = document.getElementById("output");
    
    //basic math functions
    function add(num1, num2) {
      result = display = num1 + num2; // combining result and display to a single line.
      return result;
    }
    
    function subtract(num1, num2) {
      result = display = num1 - num2;
      return result;
    }
    
    function multiply(num1, num2) {
      result = display = num1 * num2;
      return result;
    }
    
    function divide(num1, num2) {
      result =
        num2 === 0
          ? (console.log("divide by zero? Hoser!"),
            alert("cannot divide by zero"),
            0)
          : (display = num1 / num2);
    }
    
    function clear() {
      result = 0;
      display = "0";
      num1 = "";
      operand = "";
      num2 = "";
    }
    
    function performCalculation(){
      switch (operand) {
        case "+":
          add(parseFloat(num1), parseFloat(num2));
          break;
        case "-":
          if (num1 === "") num1 = 0;
          subtract(parseFloat(num1), parseFloat(num2));
          break;
        case "*":
          multiply(parseFloat(num1), parseFloat(num2));
          break;
        case "/":
          divide(parseFloat(num1), parseFloat(num2));
          break;
        default:
          console.log("switch", "Houston we have a problem!");
      }
    }
    
    function equals() {
      const calculationString = display.replace(/[^0-9+-*/.]/g, "");
      const firstCalculations = calculationString.split(/([*/])/);
      const calculations = firstCalculations.map((calculation) => {
        if (calculation.length > 2) {
          const secondCalculations = calculation.split(/([+-])/);
          return secondCalculations;    
        }
        return calculation;
      });
    
    
      if (calculations[0]==="") {
        result = 0;
      }else {
        result = parseFloat(calculations[0]);
      }
      display = result.toString();
    
      for (let i = 1; i < calculations.length; i += 2) {
        operand = calculations[i];
        num1 = display;
        num2 = calculations[i + 1];
    
        performCalculation();
    
        display = result.toString();
      }
    outputElement.textContext = display;
    }
    
    let buttons = document.getElementById("buttons");
    buttons.addEventListener("click", clickHandler);
    
    function clickHandler(e) {
      if (display === "0") {
        display = "";
      }
      let clicked = e.target;
      if (clicked.classList.contains("nbr")) {
        let number = clicked.id;
        // if (display === "0") {
        //   display = "";
        // }
        if (operand === "") {
          if (!num1.includes(".") || num1.split(".")[1].length < 3) {
            display += number;
            num1 += number;
          }
        } else {
          if (!num2.includes(".") || num2.split(".")[1].length < 3) {
            display += number;
            num2 += number;
          }
        }
      } else if (clicked.classList.contains("oper")) {
        display += clicked.id;
        if (clicked.id === "-" && operand === "") {
          if (num1 === "") {
            num1 += "-";
          } else {
            num2 += "-";
          }
        } else {
          operand = clicked.id;
        }
      } else if (clicked.id === "equals") {
        console.log("=");
        equals();
        display = parseFloat(display).toFixed(3).toString();
        display = display.replace(/.?0*$/, "");
      } else if (clicked.id === "clear") {
        clear();
      } else if (clicked.id === "back") {
        display = display.slice(0, -1);
        if (operand === "") {
          num1 = num1.slice(0, -1);
        } else {
          num2 = num2.slice(0, -1);
        }
      } else if (clicked.id === "decimal") {
        display += ".";
        if (operand === "") {
          if (!num1.includes(".")) {
    
            num1 += ".";
          }
        } else {
          if (!num2.includes(".")) {
            num2 += ".";
          }
        }
      }
    
      outputElement.textContent = display;
    }
    <div class="display"><output id="output">Display</output></div>
        <div id="buttons">
          <div id="row1">
            <button class="oper" id="+">+</button><button class="oper" id="-">-</button><button class="oper" id="*">*</button>
          </div>
          <div id="row2">
            <button class="oper" id="/">/</button><button class="alt" id="back"><-</button><button class="alt" id="clear">C</button>
          </div>
          <div id="row3">
            <button class="nbr" id="1">1</button><button class="nbr" id="2">2</button><button class="nbr" id="3">3</button>
          </div>
          <div id="row4">
            <button class="nbr" id="4">4</button><button class="nbr" id="5">5</button><button class="nbr" id="6">6</button>
          </div>
          <div id="row5">
            <button class="nbr" id="7">7</button><button class="nbr" id="8">8</button><button class="nbr" id="9">9</button>
          </div>
          <div id="row6">
            <button class="nbr" id="0">0</button><button id="decimal">.</button><button class="alt" id="equals">=</button>
          </div>
        </div>
    Login or Signup to reply.
  2. The reason is that you are trying to do a calculation with an empty string. My fix makes it so that if the value in the array is an empty string it is replaced with 0

    In function equals() I made this change

    Example: -5-10

    let calculations = calculationString.split(/([-+*/])/);
    // [ '', "-", "5", "-", "10" ]
    
    calculations = calculations.map(x => x === '' ? 0 : x);
    // [ 0, "-", "5", "-", "10" ]
    
    let display = "0";
    let result = 0;
    let num1 = "";
    let operand = "";
    let num2 = "";
    let outputElement = document.getElementById("output");
    
    //basic math functions
    function add(num1, num2) {
        result = display = num1 + num2; // combining result and display to a single line.
        console.log("add", result, "d", display);
        return result;
    }
    
    function subtract(num1, num2) {
        result = display = num1 - num2;
        console.log("sub", result, "d", display);
        return result;
    }
    
    function multiply(num1, num2) {
        result = display = num1 * num2;
        console.log("mult", result, "d", display);
        return result;
    }
    
    function divide(num1, num2) {
        result =
            num2 === 0
                ? (console.log("divide by zero? Hoser!"),
                    alert("cannot divide by zero"),
                    0)
                : (display = num1 / num2);
    }
    
    function clear() {
        result = 0;
        display = "0";
        num1 = "";
        operand = "";
        num2 = "";
        console.log("clear", num1, operand, num2, "d", display);
    }
    
    function performCalculation() {
        switch (operand) {
            case "+":
                console.log(operand);
                add(parseFloat(num1), parseFloat(num2));
                break;
            case "-":
                console.log(operand);
                subtract(parseFloat(num1), parseFloat(num2));
                break;
            case "*":
                console.log(operand);
                multiply(parseFloat(num1), parseFloat(num2));
                break;
            case "/":
                console.log(operand);
                divide(parseFloat(num1), parseFloat(num2));
                break;
            default:
                console.log("switch", "Houston we have a problem!");
        }
    }
    
    function equals() {
        console.log("equals");
        const calculationString = display.replace(/[^0-9+-*/.]/g, "");
        console.log('cs', calculationString);
    
        // START FIX /////////////////////////////////////////
        let calculations = calculationString.split(/([-+*/])/);
    
        calculations = calculations.map(x => x === '' ? 0 : x);
        console.log('c', calculations);
        // END FIX ///////////////////////////////////////////
    
        result = parseFloat(calculations[0]);
        display = result.toString();
    
        for (let i = 1; i < calculations.length; i += 2) {
            operand = calculations[i];
            num1 = display;
            num2 = calculations[i + 1];
    
            performCalculation();
    
            display = result.toString();
        }
        outputElement.textContext = display;
    }
    
    let buttons = document.getElementById("buttons");
    buttons.addEventListener("click", clickHandler);
    
    function clickHandler(e) {
        if (display === "0") {
            display = "";
        }
        let clicked = e.target;
        if (clicked.classList.contains("nbr")) {
            let number = clicked.id;
            // if (display === "0") {
            //   display = "";
            // }
            if (operand === "") {
                if (!num1.includes(".") || num1.split(".")[1].length < 3) {
                    display += number;
                    num1 += number;
                }
            } else {
                if (!num2.includes(".") || num2.split(".")[1].length < 3) {
                    display += number;
                    num2 += number;
                }
            }
        } else if (clicked.classList.contains("oper")) {
            display += clicked.id;
            if (clicked.id === "-" && operand === "") {
                if (num1 === "") {
                    num1 += "-";
                } else {
                    num2 += "-";
                }
            } else {
                operand = clicked.id;
                console.log("CH Oper", operand);
            }
        } else if (clicked.id === "equals") {
            console.log("=");
            equals();
            display = parseFloat(display).toFixed(3).toString();
            display = display.replace(/.?0*$/, "");
        } else if (clicked.id === "clear") {
            clear();
        } else if (clicked.id === "back") {
            display = display.slice(0, -1);
            if (operand === "") {
                num1 = num1.slice(0, -1);
            } else {
                num2 = num2.slice(0, -1);
            }
        } else if (clicked.id === "decimal") {
            display += ".";
            if (operand === "") {
                if (!num1.includes(".")) {
    
                    num1 += ".";
                }
            } else {
                if (!num2.includes(".")) {
                    num2 += ".";
                }
            }
        }
    
        console.log("clicked", clicked, "d", display);
        console.log(num1, operand, num2, "d", display);
        outputElement.textContent = display;
    }
    <div class="display"><output id="output">Display</output></div>
    <div id="buttons">
        <div id="row1">
            <button class="oper" id="+">+</button><button class="oper" id="-">-</button><button class="oper"
                id="*">*</button>
        </div>
        <div id="row2">
            <button class="oper" id="/">/</button><button class="alt" id="back"><-< /button><button class="alt"
                        id="clear">C</button>
        </div>
        <div id="row3">
            <button class="nbr" id="1">1</button><button class="nbr" id="2">2</button><button class="nbr"
                id="3">3</button>
        </div>
        <div id="row4">
            <button class="nbr" id="4">4</button><button class="nbr" id="5">5</button><button class="nbr"
                id="6">6</button>
        </div>
        <div id="row5">
            <button class="nbr" id="7">7</button><button class="nbr" id="8">8</button><button class="nbr"
                id="9">9</button>
        </div>
        <div id="row6">
            <button class="nbr" id="0">0</button><button id="decimal">.</button><button class="alt"
                id="equals">=</button>
        </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search