Why wont my calculator function complete the math?
Why wont my calculator function complete the math?
Hi so I'm having trouble figuring out why my function will do the division but leave the multiplication as an array without completing the math. Here's the code:
const mathObj = {
"*": function(a , b) {return a * b},
"/": function(a , b) {return a / b},
"+": function(a , b) {return a + b},
"-": function(a , b) {return a - b}
}
const arr = [ 10, "/" , 2 , "*" , 10 , "/" , 2 ];
function solveTheMath(arr) {
const len = arr.length;
for(let i = 0 ; i < len ; i++){
if(arr[i] === "/" || arr[i] === "*"){
const sliced = arr.slice(i - 1 , i + 2);
var mathResult = mathObj[arr[i]](sliced[0], sliced[2]);
arr.splice(i - 1 , 3, mathResult);
console.log(arr);
//[5*5]
}
}
}
solveTheMath(arr);
Why doesn't the multiplication work but the division does?
I plan on manually populating the array with click event listeners.
– Michael Lewell
Jul 2 at 14:26
1 Answer
1
My initial answer, while it did solve the issue, wasn't that correct. You wanted to use an iterative approach by the look of things (i.e. using a loop to navigate through the initial array and solve all operations before returning the result).
So I replied to you:
Both operations work, the problem is that you're only calling solveTheMath
once.
solveTheMath
You need to call your function again to solve the array you have constructed. If the constructed array is made of only one element that means the process has reached the end of the computing, you can, therefore, return the first (and only element) of the array.
You are solving the problem in a recursive manner:
const mathObj = {
"*": function(a , b) {return a * b},
"/": function(a , b) {return a / b},
"+": function(a , b) {return a + b},
"-": function(a , b) {return a - b}
}
const arr = [ 10, "/" , 2 , "*" , 10 , "/" , 2 ];
function solveTheMath(arr) {
const len = arr.length;
for(let i = 0 ; i < len ; i++){
if(arr[i] === "/" || arr[i] === "*"){
const sliced = arr.slice(i - 1 , i + 2);
var mathResult = mathObj[arr[i]](sliced[0], sliced[2]);
arr.splice(i - 1 , 3, mathResult);
if(arr.length == 1) {
return arr[0]; // <- no more calculations needed, return result
} else {
return solveTheMath(arr); // <- more calculations needed, call it again
};
}
}
}
console.log(solveTheMath(arr))
But actually that isn't correct, you can use both approaches: recursive and iterative to solve this problem. My initial answer provided a poor solution: I kept your for
loop and called the function again to solve the remaining operations that were in the array. That wasn't necessary because the for
loop only looped to find the second item and stopped. Anyway, here's a clearer answer, highlighting both approaches.
for
for
Note: I have renamed solveTheMath
to calculate
and mathObj
to operations
.
solveTheMath
calculate
mathObj
operations
That's the approach you went for with your question. Because you are using a for
loop to calculate all operations on a single function call (so the function isn't calling itself over and over).
for
I recommend using a while
loop for this because **you will have a hard time looping arr
when it gets modified (you are replacing three elements with one on each loop).
while
arr
I'll take the array [10, "/", 2, "*", 10, "/", 2]
as the starting array to show the process step by step. You can solve the first operation of the provided array. For example, given: , calculate
will calculate the first operation here: 10, "/", 2
[10, "/", 2, "*", 10, "/", 2]
calculate
10, "/", 2
While the array contains more than one element we will do the following:
the first three elements of the array contain: two factors and an operator sign. By slicing the array we can extract those values and save them. I'm using a destructuring assignment to make it more verbose:
const [a, operator, b] = arr.slice(0, 3);
here a = 10
, operator = "/"
and b = 2
a = 10
operator = "/"
b = 2
we will calculate the result of this operation with this line:
const result = operations[operator](a, b);
result = 5
(cf: 10 / 2
)
result = 5
10 / 2
then replace the first three elements of the array with the integer result
:
result
arr.splice(0, 3, result);
at this point, arr
is equal to [5, "*", 10, "/", 2]
arr
[5, "*", 10, "/", 2]
The block has been executed, the while
condition is checked again. arr
does contain more than one element so the block is executed again. Remember, at this point arr
is equal to [5, "*", 10, "/", 2]
, not to [10, "/", 2, "*", 10, "/", 2]
(we are making progress in the calculation).
while
arr
arr
[5, "*", 10, "/", 2]
[10, "/", 2, "*", 10, "/", 2]
At the end of the second loop we have arr
that is equal to [50, "/", 2]
.
arr
[50, "/", 2]
A loop after that it is equal to [25]
.
[25]
The while
condition isn't met anymore because arr
only contains one element, the while
loop has stopped and the result can be returned.
while
arr
while
const operations = {
"*": (a, b) => a * b,
"/": (a, b) => a / b,
"+": (a, b) => a + b,
"-": (a, b) => a - b
}
const calculate = arr => {
while(arr.length > 1) { // <- while there are calculations to be done, repeat this block
const [a, operator, b] = arr.slice(0, 3);
const result = operations[operator](a, b);
arr.splice(0, 3, result);
}
return arr[0]; // <- no more operations to be done, return result
}
console.log(calculate(
[10, "/", 2, "*", 10, "/", 2]
));
We can use a recursive approach: the function will only calculate the first operation of the provided array and return a new array with the result of this first operation.
Here is an example:
Same as in the iterative array, given the input [10, "/", 2, "*", 10, "/", 2]
we will first take the first two factors and operator sign by slicing the array. Then we will calculate the result of the operation. Finally, we'll replace the first three elements of the array with this result:
[10, "/", 2, "*", 10, "/", 2]
const [a, operator, b] = arr.slice(0, 3);
const result = operations[operator](a, b);
arr.splice(0, 3, result);
then we check the length of this array:
if it contains only one element, it can be returned
else if it doesn't (in our case) we call the function again (this time on [5, "*", 10, "/", 2]
).
[5, "*", 10, "/", 2]
So the function runs again with a new input and arr
becomes [50, "/", 2]
which has more than one element so the function needs to be called again (with [50, "/", 2]
as input)
arr
[50, "/", 2]
[50, "/", 2]
Now, arr
is [25]
it only contains one element, the result can be returned (25
).
arr
[25]
25
const operations = {
"*": (a, b) => a * b,
"/": (a, b) => a / b,
"+": (a, b) => a + b,
"-": (a, b) => a - b
}
const calculate = arr => {
const [a, operator, b] = arr.slice(0, 3);
const result = operations[operator](a, b);
arr.splice(0, 3, result);
if (arr.length == 1) {
return arr[0]; // <- no more calculations needed, return result
} else {
return calculate(arr); // <- more calculations needed, call it again
}
}
console.log(calculate(
[10, "/", 2, "*", 10, "/", 2]
));
You can see both methods are quite similar: the main process is the same but the way they handle the end of execution is different. In this case, both are reasonable to use. The iterative approach may seem more natural to you at first. However remember that recursion can allow you to solve more complicated problems. For example, if you would like to implement a kind parenthesis system in your function:
How would you go about calculating: 10*(2+2)/2
? calculate([10, "*", 2, "+", 2, "/", 2])
would obviously return 11
...
10*(2+2)/2
calculate([10, "*", 2, "+", 2, "/", 2])
11
Take the input [[10, "+", 2], "/", 2]
instead, that makes more sense! How can we calculate the correct result?
[[10, "+", 2], "/", 2]
Well with our recursive approach this can be implemented quite easily: if a
or/and b
are arrays then we reassign them by calling calculate
on them. That's all:
a
b
calculate
if(a.constructor == Array) {
a = calculate(a);
}
if(b.constructor == Array) {
b = calculate(b);
}
const operations = {
"*": (a, b) => a * b,
"/": (a, b) => a / b,
"+": (a, b) => a + b,
"-": (a, b) => a - b
}
const calculate = arr => {
let [a, operator, b] = arr.slice(0, 3);
if(a.constructor == Array) {
a = calculate(a);
}
if(b.constructor == Array) {
b = calculate(b);
}
const result = operations[operator](a, b);
arr.splice(0, 3, result);
if (arr.length == 1) {
return arr[0]; // <- no more calculations needed, return result
} else {
return calculate(arr); // <- more calculations needed, call it again
}
}
console.log(calculate(
[[10, "+", 2], "/", 2]
));
Adding those two if
blocks in the while loop of the iterative approach would have worked. But then you would be left with a... recursive function. That's why you may want to go straight off with the recursive approach. That allows you to expand your code more easily.
if
Ah thank you so much. I'm quite a noob at javascript what does recursive manner mean ?
– Michael Lewell
Jul 2 at 10:27
@MichaelLewell Recursion: An act of a function calling itself.. when calling
solveTheMath
you are constructing a new array that needs to be calculated too. Therefore you have to call the function again this time on the new array. It will call itself until the array has one element and one only. You may want to look at easier examples on the internet. That will help you understand the concept better.– Ivan
Jul 2 at 10:36
solveTheMath
@MichaelLewell, I'll edit my answer with a detailed explanation
– Ivan
Jul 2 at 10:39
okay thanks i'll look into it now. I appreciate the help.
– Michael Lewell
Jul 2 at 10:43
@MichaelLewell, Here it is as promised: a detailed explanation.
– Ivan
Jul 2 at 12:25
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Is this later going to be a prompt for inputting the math function? Or do you plan on always manually populating your array?
– Matt
Jul 2 at 12:25