0

I'm trying to build the classic javascript calculator that has to receive two numbers and an operation and return the result. It does not need to evaluate the precedence of the operations, it must only return the operations in order.

I decided to approach it using an array to store the values of the button clicks (numbers, operations, clear, and equal/result). But now I'm having trouble in storing and picking apart the fistNumber from the secondNumber. And the operation that must be made.

function operate(num1, num2, operator) {
  return operator(num1, num2);
}


let bubblePad = document.querySelector("#pad");
let display = document.querySelector("#display")
let show = document.querySelector(".show");
let btn = document.querySelectorAll(".btn");
let equal = document.querySelector(".doubleBtn");

bubblePad.addEventListener("click", makeArray)

let args = []
let firstNumber = ""
let secondNumber = ""
let operator = ""

//functions
function makeArray(e) {
  switch (true) {
    case e.target.id === "":
    case e.target.id === "pad":
      console.log("the empty click was logged")
      break;
      /*
      You’ll need to store the first number that is input into the calculator when a user presses an operator, and also save which operation has been chosen and then operate() on them when the user presses the “=” key.*/

    case e.target.id === "*":
    case e.target.id === "/":
    case e.target.id === "+":
    case e.target.id === "-":
    case e.target.id === "%":
      //IM STUCK - how do i split my array here, pick the operation to be made, and generate a new array that will use the next clicks to build the secondNumber
      break;
    case e.target.id === ".":
      args.push(e.target.id);
      //desable the dot if it was clicked before
      break;
    case e.target.id === "CE":
      args.pop()
      console.log(args)
      break;
    case e.target.id === "C":
      args = [];
      console.log(args);
      show.textContent = "";
      break;
    case e.target.id === "=":
      //operate(firstNumber,secondNumber,operator)
      break;
    default:
      args.push(e.target.id)
      console.log(args);
      firstNumber = args.join("");
      show.textContent = firstNumber;
  }

}
<div id="calculator">
  <div id="display">
    <p class="show"> 789*3-2+5*7</p>
  </div>
  <div id="pad" class="pad">
    <div>
      <p class="btn" id="C">C</p>
    </div>
    <div>
      <p class="btn" id="CE">CE</p>
    </div>
    <div>
      <p class="btn" id="%">%</p>
    </div>
    <div>
      <p class="btn" id="/">/</p>
    </div>
    <div>
      <p class="btn" id="7">7</p>
    </div>
    <div>
      <p class="btn" id="8">8</p>
    </div>
    <div>
      <p class="btn" id="9">9</p>
    </div>
    <div>
      <p class="btn" id="*">*</p>
    </div>
    <div>
      <p class="btn" id="4">4</p>
    </div>
    <div>
      <p class="btn" id="5">5</p>
    </div>
    <div>
      <p class="btn" id="6">6</p>
    </div>
    <div>
      <p class="btn" id="-">-</p>
    </div>
    <div>
      <p class="btn" id="1">1</p>
    </div>
    <div>
      <p class="btn" id="2">2</p>
    </div>
    <div>
      <p class="btn" id="3">3</p>
    </div>
    <div>
      <p class="btn" id="+">+</p>
    </div>
    <div>
      <p class="btn" id=".">.</p>
    </div>
    <div>
      <p class="btn" id="0">0</p>
    </div>
    <div class="doubleBtn">
      <p id="=">=</p>
    </div>

  </div>
</div>
tripathiakshit
  • 624
  • 5
  • 17

3 Answers3

0

I would much prefer to do this as a string. Storing it in an array seems like the simplest solution, but is actually more of a headache for invalid operations and when you end up needing to do order of operations stuff. If you simply concatenate operations to a string it will make life a lot easier. You could turn it into reverse polish notation and evaluate it using a stack as shown here, but that might be overkill.

Most of the time I would never recommend doing this, but as long as you can assure the input isn't malicious (which in this case you can) and it's just basic arithmetic you're doing, I would say it's not a horrible use for eval. An example if you needed to output a string would be:

let result = eval('2+5*6'); //outputs 32
///isNaN means the input was invalid
return Number.isNaN(result) ? 'Error' : result.toString();
  • I'm sorry I didn't mention before, but eval is specifically forbidden in this case. This thread explains eval in this situation and comes to the same conclusion as you have: https://stackoverflow.com/questions/6479236/calculate-string-value-in-javascript-not-using-eval?noredirect=1&lq=1 – Leonardo Nobre Ghiggino Jul 30 '20 at 09:46
  • Ah, ok. I do reverse Polish notation then. –  Jul 30 '20 at 14:54
0

If you're just calculating based on two numbers you could maybe do something like this:

let numStringOne = ''
let numStringTwo = ''
let operation = false
for (let i = 0; i < args.length; i++) {
    // if value is number and operation is false 
    // then assign to 1st number
    if (!Number.isNaN(+args[i]) && !operation) {
        numStringOne += args[i]
    } 
    // if value is number and operation is no
    // longer false then assign to 2nd number
    else if (!Number.isNaN(+args[i]) && operation) {
        numStringTwo += args[i]
    } 
    // add operation symbol
    else {
        operation = arr[i]
    }
}
// convert string numbers to numbers
firstNumber = +numStringOne
secondNumber = +numStringTwo
jason_r
  • 345
  • 1
  • 11
0

I have taken a different approach to this problem, see if you prefer this instead. We keep a running total in one variable, and just perform the operations using that as the first number.

I also broke down your event listener into 3 different functions because they perform 3 different kinds of things, so I had to add a few classes to your HTML.

P.S.: I completely understand if this isn't the answer you were looking for, but it is easier to construct logic like this instead of writing all info into an array and then splitting it into first and second numbers. And of course feel free to remove the CSS.

const show = document.querySelector("#show");
const numbers = document.getElementsByClassName("num");
const operators = document.getElementsByClassName("op");
const controls = document.getElementsByClassName("ctr");

let currentResult = 0
let currentInput = []
let currentOp = "";

function inputNum(e) {
  const num = e.target.id;

  if (num === "." && currentInput.includes(num)) {
    alert("Only one decimal is allowed");
  } else {
    currentInput.push(e.target.id);
  }

  show.innerText = parseInt(currentInput.join(""));
  console.log(currentInput);
}

function inputOp(e) {
  const op = e.target.id;
  const curInput = currentInput.length !== 0 ? parseInt(currentInput.join("")) : 0;

  if (currentResult === 0 && currentOp === "") {
    currentResult = curInput;
  } else {
    currentResult = performOperation(currentResult, curInput, currentOp);
  }

  show.innerText = currentResult + " " + op;
  currentOp = op;
  currentInput = [];
}

function inputCtr(e) {
  const input = e.target.id;

  switch (input) {
    case "CE":
      currentInput.pop();
      show.innerText = currentInput.join("");
      break;
    case "C":
      currentInput = [];
      show.innerText = "0";
      break;
    case "=":
      const curInput = currentInput.length !== 0 ? parseInt(currentInput.join("")) : 0;
      currentResult = performOperation(currentResult, curInput, currentOp);
      show.innerText = currentResult;
  }
}

function performOperation(num1, num2, oper) {
  switch (oper) {
    case "+":
      return num1 + num2;
    case "-":
      return num1 - num2;
    case "*":
      return num1 * num2;
    case "/":
      return num1 / num2;
    case "%":
      return num1 % num2;
  }
}

for (item of numbers) {
  item.addEventListener("click", inputNum, false);
}
for (item of operators) {
  item.addEventListener("click", inputOp, false);
}
for (item of controls) {
  item.addEventListener("click", inputCtr, false);
}
.pad {
  display: grid;
  grid-template-columns: repeat(4, 5ch);
  grid-template-rows: repeat(5, 5ch);
  grid-gap: 2px;
}

.doubleBtn {
  grid-column: 3 / span 2;
}

.btn {
  color: white;
  border: none;
}

.ctr {
  background-color: orange;
}

.num {
  background-color: black;
}

.op {
  background-color: darkgray;
}
<div id="calculator">
  <div id="display">
    <p id="show">0</p>
  </div>
  <div id="pad" class="pad">
    <button class="btn ctr" id="C">C</button>
    <button class="btn ctr" id="CE">CE</button>
    <button class="btn op" id="%">%</button>
    <button class="btn op" id="/">/</button>
    <button class="btn num" id="7">7</button>
    <button class="btn num" id="8">8</button>
    <button class="btn num" id="9">9</button>
    <button class="btn op" id="*">*</button>
    <button class="btn num" id="4">4</button>
    <button class="btn num" id="5">5</button>
    <button class="btn num" id="6">6</button>
    <button class="btn op" id="-">-</button>
    <button class="btn num" id="1">1</button>
    <button class="btn num" id="2">2</button>
    <button class="btn num" id="3">3</button>
    <button class="btn op" id="+">+</button>
    <button class="btn num" id=".">.</button>
    <button class="btn num" id="0">0</button>
    <button class="doubleBtn btn ctr" id="=">=</button>
  </div>
</div>
tripathiakshit
  • 624
  • 5
  • 17