Reputation: 23
I need help with my calculator. I don't know how to string together several operations. Can someone help me out with this or give some tips how to work forward. My calculator can only 3 + 3 but not 3 + 3 + 3 etc several operations.
My calculator works like this:
const operator = document.querySelectorAll('.operator');
const equalsKey = document.getElementById('equalsKey');
const display = document.getElementById('result');
const numbers = document.querySelectorAll('.number');
const operators = document.querySelectorAll('.operator');
const clear = document.getElementById('clear');
let firstNumber;
let currentOperator;
let secondNumber;
let tempArray = [];
numbers.forEach((number) => {
number.addEventListener('click', (e) => {
tempArray.push(e.target.value); // store value to temparray
console.log(tempArray);
display.innerHTML = Number(tempArray.join("")); // display number from array
});
});
operators.forEach((operator) => {
operator.addEventListener('click', (e) => {
currentOperator = e.target.value; // store current operator
console.log(currentOperator);
firstNumber = Number(tempArray.join("")); // parse array to integers
console.log(firstNumber);
tempArray = []; // empty array
});
});
clear.addEventListener('click', function () {
display.textContent = '0';
})
function calculate() {
secondNumber = Number(tempArray.join(""));
let result = operate(parseFloat(firstNumber), parseFloat(secondNumber), currentOperator);
display.textContent = result;
}
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
return a / b;
}
function operate(num1, num2, operator) {
switch (operator) {
case "+":
return add(num1, num2);
case "-":
return subtract(num1, num2);
case "*":
return multiply(num1, num2);
case "÷":
return divide(num1, num2);
}
};
equalsKey.addEventListener('click', calculate);
https://codepen.io/alcatel1962/pen/LYONKKW
I don't know how to think when try to string together several operations.
Upvotes: 2
Views: 1504
Reputation: 134
Good job so far! to tackle your problem, you should put your knowledge of different kinds of loops which should allow you to solve this problem rather easily.
here's the link to a pen forked from yours that tackles your problem through a different approach(although a very lazy one). My approach tries to mimic the behavior of the Google Calculator(the one you get when you google for "calculator")
Note however that I have not considered negative numbers and floating-point numbers, Also there needs to be validation as shown in the comments of the code if you are to follow this approach, And of course, you will have to push the result of the previous calculation as the first number to the array after "=" is pressed, Try to identify/solve these problems/improvements by yourself ;) good luck!
const equalsKey = document.getElementById('equalsKey');
const display = document.getElementById('result');
const clear = document.getElementById('clear');
const numbersAndOperators = document.querySelectorAll('.operator, .number');
let firstNumber;
let currentOperator;
let secondNumber;
let tempArray = [];
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
return a / b;
}
// When the user hits any button or operator push them to the array after validating.
numbersAndOperators.forEach((numberOrOperator) => {
numberOrOperator.addEventListener('click', (e) => {
// TODO: validate next input here
tempArray.push(e.target.value); // store value to temparray
console.log(tempArray);
display.innerHTML = tempArray.join(""); // display from array
});
});
// Go through the array and identify if operators according to the order of
// BODMAS is present, and execute that operator for the operands left and right to it if found.
function validateBODMAS(inputs){
let modifiedInputs = inputs
if(modifiedInputs.includes("÷")){
modifiedInputs = runOpp(modifiedInputs,"÷",divide)
}
if (modifiedInputs.includes("*")){
modifiedInputs = runOpp(modifiedInputs,"*",multiply)
}
if (modifiedInputs.includes("+")){
modifiedInputs = runOpp(modifiedInputs,"+",add)
}
if (modifiedInputs.includes("-")){
modifiedInputs = runOpp(modifiedInputs,"-",subtract)
}
return modifiedInputs;
}
// Run the operator for all opperands next to a particular operator.
// replace the result with operands and operator.
function runOpp(inputarr,oppSymbol,oppCallback){
while(inputarr.includes(oppSymbol)){
const indexOpp = inputarr.indexOf(oppSymbol)
inputarr.splice(indexOpp - 1,3,oppCallback(Number(inputarr[indexOpp - 1]) , Number(inputarr[indexOpp + 1])))
console.log(inputarr)
}
return inputarr
}
function calculate() {
result = validateBODMAS(tempArray)
display.textContent = result;
}
clear.addEventListener('click', function () {
display.textContent = '';
tempArray = [];
})
equalsKey.addEventListener('click', calculate);
Upvotes: 2
Reputation: 3691
Improving on the this existing code-pen solution, the below has been created.
To execute / view the code snippet below, please switch to FULL PAGE.
Code Snippet
const operator = document.querySelectorAll('.operator');
const equalsKey = document.getElementById('equalsKey');
const display = document.getElementById('result');
const numbers = document.querySelectorAll('.number');
const operators = document.querySelectorAll('.operator');
const clear = document.getElementById('clear');
const deci = document.getElementById('deci');
// to view debug info, change class 'hideDebug' display-style in CSS
const debug = document.getElementById('debug');
deci.addEventListener('click', () => alert('decimals not yet added...'));
// a temp array to hold input characters
let cache = [];
// a closure to handle creating, manipulating the parse-tree
function parser() {
// a tree-structure to use for storing & parsing operators, operands.
const myTree = { root: null, left: null, right: null };
// reset tree to original state
const reset = (mt) => {
mt.root = null; mt.left = null; mt.right = null;
};
// add a new operand or operator into the proper location
// uses BODMAS (also called PEDMAS, PEMDAS, BOMDAS, etc)
// basically division-multiplication takes priority over addition-subtraction
const add = (el, type, nt) => {
if (type === 'operand') {
if (nt.left === null) nt.left = +el;
else if (nt.right === null) nt.right = +el;
else add(el, type, nt.right);
} else {
if (nt.root === null) nt.root = el;
else if ('+-'.includes(nt.root) && '/*'.includes(el)) {
// if current operator is / or * and previous is + or -
// structure tree so that the / or * takes precedence
const t = structuredClone(nt.right);
nt.right = { root: el, left: t, right: null };
} else {
// for all other cases left side calculation takes precedence
const t = structuredClone(nt);
nt.left = t;
nt.root = el;
nt.right = null;
};
};
};
// helper method to quickly compute simple operations
const compute = (op1, op, op2) => {
switch (op) {
case '+': return op1 + op2;
case '-': return op1 - op2;
case '*': return op1 * op2;
case '/': return op1 / op2;
}
};
// recursive method to evaluate the parse-tree
const evaluate = (et) => (
compute(
Number.isFinite(et.left) ? et.left : evaluate(et.left),
et.root,
Number.isFinite(et.right) ? et.right : evaluate(et.right)
)
);
// return object with relevant methods
return {
isEmpty: () => myTree.root === null,
addToTree: (el, type = 'operand') => {
add(el, type, myTree);
},
showTree: () => (JSON.stringify(myTree)),
clearTree: () => {reset(myTree)},
calculate: () => evaluate(myTree)
};
};
const myParser = parser();
// handle clear button click
clear.addEventListener('click', function () {
display.textContent = '0';
myParser.clearTree();
debug.innerHTML = myParser.showTree();
cache = [];
});
// for each number clicked,
// cache the value, and update display
numbers.forEach(el => {
el.addEventListener('click', (ev) => {
if (cache.length === 0 && myParser.isEmpty()) display.innerHTML = ev.target.value;
else display.innerHTML += ev.target.value;
cache.push(ev.target.value);
});
});
// for each operator clicked,
// process existing cache and operator into parse-tree
operator.forEach(el => {
el.addEventListener('click', (ev) => {
if (cache.length > 0 || ev.target.value === '-') {
if (cache.length === 0) { // negative number as operand-1
display.innerHTML = '0-';
cache.push(0);
} else if (!('+-*/'.includes(cache.slice(-1)))) display.innerHTML += ev.target.value;
myParser.addToTree(cache.join(''));
cache = [];
myParser.addToTree(ev.target.value, 'operator');
debug.innerHTML = myParser.showTree();
}
});
});
// calculate method
// add last operand to parse-tree, clear cache
// obtain the calculated-result, display & cache it
// finally, clear the parse-tree
const calculate = () => {
myParser.addToTree(cache.join(''));
cache = [];
debug.innerHTML = myParser.showTree();
const calcResult = myParser.calculate();
display.innerHTML = calcResult;
cache.push(calcResult);
myParser.clearTree();
};
equalsKey.addEventListener('click', calculate);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 10px;
}
body {
font-family: "Open Sans", sans-serif;
}
p {
font-size: 16px;
line-height: 1.5;
}
img {
width: 100%;
}
.container {
max-width: 900px;
margin: 0 auto;
padding: 0 20px;
}
.box {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.calc {
width: 300px;
height: 350px;
margin: 0 auto;
background-color: #ccc;
padding: 15px;
border-radius: 3px;
position: relative;
}
.result {
color: rgb(68, 66, 66);
background-color: #fff;
height: 45px;
width: 70%;
border-radius: 3px;
margin-bottom: 5px;
border: 2px solid grey;
padding: 10px;
overflow: hidden;
}
.clear {
width: 20%;
height: 45px;
padding: 10px;
position: absolute;
right: 20px;
top: 5px;
border-radius: 3px;
cursor: pointer;
font-size: 16px;
}
.keys {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 5px;
font-size: 16px;
}
.keys button {
list-style: none;
width: 50px;
background-color: #fff;
margin: 10px 5px;
padding: 10px;
/* border-bottom: 1px solid #dadedc; */
border-radius: 3px;
color: rgb(68, 66, 66);
text-align: center;
cursor: pointer;
transition: all 0.1s;
}
.keys button:hover {
background-color: #dadedc;
color: #000;
}
.keys .equals {
background-color: blue;
color: #fff;
}
.hideDebug { display: none; }
<div class='hideDebug' id='debug'>aa</div>
<section>
<div class="container box">
<div class="calc">
<div class="result">
<p id="result">0</p>
</div>
<button class="clear" id="clear">C</button>
<ul class="keys">
<button class="number" value="1">1</button>
<button class="number" value="2">2</button>
<button class="number" value="3">3</button>
<button class="operator" value="+">+</button>
<button class="number" value="4">4</button>
<button class="number" value="5">5</button>
<button class="number" value="6">6</button>
<button class="operator" value="-">-</button>
<button class="number" value="7">7</button>
<button class="number" value="8">8</button>
<button class="number" value="9">9</button>
<button class="operator" value="*">*</button>
<button class="number" value="0">0</button>
<button id='deci' class="decimal" value=".">.</button>
<button class="equals" id="equalsKey" value="=">=</button>
<button class="operator" value="/">÷</button>
</ul>
</div>
</div>
</section>
Explanation
Detailed comments added inline in the above code-snippet
Upvotes: 1