javascript: passing basic math operations to function - javascript

I am programming a calculator that takes a string representing a calculation and outputs the result (like the eval function).
At some point in my code I define the meaning of the different operations like this:
const _ops = [
[
["^", (a, b) => (+a) ** +b],
],
[
["*", (a, b) => +a * +b],
["/", (a, b) => +a / +b],
["%", (a, b) => +a % +b],
],
[
["+", (a, b) => +a + +b],
["-", (a, b) => +a - +b],
],
];
As you can see I am repeating the function part every time while only one character changes (except for the first time...)
(a, b) => +a ${operation} +b
Do you have any idea how I could do this without repeating the function declaration every time?
PS: If you can think of a better title, feel free to change it.

No, this is not possible in JavaScript without eval. This is because there is no way to treat an operator as a function. This is not the case e.g. in Haskell, where (*) represents the multiplication function, which you can carry around as a value, apply a value to get a partially-applied operator, and apply another value to get the result.

Using eval would look kind of like this.
const dynamicCalculation = (a ,b, operator) => {
if (operator === "^") operator = "**"
return eval("${+a} ${operator} ${+b}")
}
usage:
dynamicCalculation(1,2,"+") // => 3
dynamicCalculation(1,2,"-") // => -1
dynamicCalculation(1,2,"*") // => 2
switch:
const dynamicCalculation = (a ,b, operator) => {
let output;
switch(operator){
case "+":
output = a + b;
break;
case "-":
output = a - b;
break;
case "*":
output = a * b;
break;
case "/":
output = a / b;
break;
case "^":
output = a ** b;
break;
case "%":
output = a % b;
break;
}
return output
}

Related

Trying to add more cases, not working as intended

My goal is to make a little program that understands human words.
I decided to start out with simple math before making other stuff but something is wrong.
So far the human reader can do addition, but I want it do more operations like subtraction, multiplication, division, etc. I have this part of the code that handles cases, basically if you tell it to 'add ...' it should add, you get the idea, And it looks like this:
switch (operator) {
case "add":
return numbers.reduce((a, b) => Number(a) + Number(b));
Great, so I figured I should just make another case and just do "subtract" and just do Number(a) - Number(b) right?
Well thats where it goes flop, if I tell it to subtract 2 from 5, it gives me a response that looks like this:
undefined
When what I want it to say is:
-3
How can I solve this issue?
Full Source Code:
function output(func) {
console.log(func)
}
function read(str) {
const dl = str.split(' ');
const operator = dl.shift(x => x.includes("add", "sub", "mul", "div"))
const numbers = dl.filter(x => Number(x))
switch (operator) {
case "add":
return numbers.reduce((a, b) => Number(a) + Number(b));
case "subtract":
return numbers.reduce((a, b) => Number(a) - Number(b));
}
}
const result = read(
"add 6 and 4"
)
output(result)
Looks like it works fine.
function output(func) {
console.log(func)
}
function read(str) {
const dl = str.split(' ');
const operator = dl.shift(x => x.includes("add", "sub", "mul", "div"))
const numbers = dl.filter(x => Number(x))
switch (operator) {
case "add":
return numbers.reduce((a, b) => Number(a) + Number(b));
case "subtract":
return numbers.reduce((a, b) => Number(a) - Number(b));
}
}
output(read(
"add 6 and 4"
))
output(read(
"subtract 6 and 4"
))
output(read(
"subtract 2 and 5"
))
output(read(
"sub 2 and 5"
))

Chaining Multiple operators in Javascript calculator

I cant seem to get my calculator to chain multiple operators together for example 5x5x5 should equal 125 but it just calculates 5x5 and will no longer calculate decimal numbers. Could anyone point me in the correct direction.
I have tried to assign the result to the firstNum but it seems to break the code.
button.forEach((button) =>
button.addEventListener('click', ()=> displayTotal(button.innerHTML)));
clearBtn.addEventListener('click', clear);
decmialBtn.addEventListener('click', appendDecimal);
functionBtn.forEach((button) =>
button.addEventListener('click', () => setOperator(button.innerHTML)));
equalsBtn.addEventListener('click', calculate)
let firstNum = "";
let secondNum = "";
let operatorSelection = "null";
let result = "";
function add(a, b){
return a + b
}
function subtract(a,b){
return a - b
}
function divide (a,b)
{
return a / b
}
function times (a,b){
return a*b
}
function operator(operator, numa, numb) {
switch(operator){
case '+':
return add(numa, numb);
break;
case '-':
return subtract(numa, numb);
break;
case '/':
return divide(numa,numb);
break;
case '*':
return times(numa,numb)
break;
}
}
function displayTotal (number){
userDisplay.value += number;
}
function clear (){
userDisplay.value = "";
}
function appendDecimal(){
userDisplay.value += ".";
}
function setOperator(operator){
firstNum = parseInt(userDisplay.value);
operatorSelection = operator;
result = firstNum;
clear()
}
function calculate(){
for (i = 0; i < 100; i++)
secondNum = parseInt(userDisplay.value);
result = operator(operatorSelection, firstNum, secondNum);
userDisplay.value = result;
[i];
}
For a simple JS calculator you could use the eval() function and it will handle all the operators for you taking PEMDAS in consideration, but you have to ensure the inpput is valid or it you won't work.
I have one on my Github if want an example:
https://github.com/gustavo-shigueo/calculator
Note: don't worry about the regexes I used, they are there just to prevent the user from typing stuff that would break the calculator
Here is an example of how I would do it:
operations = [
["*", (a, b) => +a * +b],
["/", (a, b) => +a / +b],
["+", (a, b) => +a + +b],
["-", (a, b) => +a - +b],
];
const calc = calcString => {
const split = [...calcString].reduce((prev, cur) => Number.isInteger(+cur) && Number.isInteger(+prev[prev.length - 1]) ? [...prev.slice(0, -1), prev[prev.length - 1] + cur] : [...prev, cur], [""]);
operations.forEach(([operator, funct]) => {
for (let i = 1; i < split.length - 1; i++) {
if (split[i] === operator) {
split[i - 1] = funct(split[i - 1], split[i + 1]);
split.splice(i, 2);
i--;
}
}
})
return split;
};
It doesn't support numbers with a dot/comma in them (e.g. 3.13) and no braces.
some explanation:
const split, I split the given string in numbers and operations, "3+45*4" would result in ["3", "+", "45", "*", "4"].
go over each operation in the given order to first do the "point calculation" and then + and -.
we go over the in 1. created array and look for our operation.
if we find one, we lool at the number before and after the operation and calculate the result given the 2 numbers and our operation type.
we overwrite those 3 elements with the result of our calculation.
we are done if all operations are done
I also have a more advanced version if you are interested

How can I add operation symbols (+, *, -, /) to an array in Javascript

How can I add operation symbols (+, *, -, /) to an array in JavaScript? I am trying to get a randomized symbol from an array and I have 4 options: +, *, -, /.
var operations = [*, +, -, /]
It does not detect it as an array of symbols though.
I am trying to create a webapp for me to practice math. I want to get two random numbers that will get operated (added, multiplied, divided, or subtracted) by a random operation (-, /, *, or +). so the equation becomes totally random, example: 472 * 820 =.
You need quotes around strings.
var operations = ['*', '+', '-', '/'];
var randIdx = Math.random() * operations.length | 0;
var randOp = operations[randIdx];
console.log(randOp);
To actually perform random operations, you do not need the operators themselves: you can just generate a random integer from 0-3 and map each integer to an operation yourself.
var rand1 = Math.random() * 50 | 0 + 50,
rand2 = Math.random() * 50 | 0 + 50,
randOp = Math.random() * 4 | 0;
console.log(rand1, ["+", "-", "*", "/"][randOp], rand2, "=");
switch(randOp){
case 0:
console.log(rand1 + rand2);
break;
case 1:
console.log(rand1 - rand2);
break;
case 2:
console.log(rand1 * rand2);
break;
default:
console.log(rand1 / rand2);
}
The simple answer to "How can I add operation symbols (+, *, -, /) to an array in Javascript" is that you can't do that.
An operator is a symbol in the language, but it is not a string, number, boolean, or an Object, so it can't be part of a data structure.
You can put some kind of representation of an operator in an array, such as a string:
let operations = [ '*', '+', '-', '/' ];
There are several pretty good answers, and with your added explanation of your goal I would recommend something that, in some ways, combines some elements from those answers.
You want to display two random numbers with the choice of a random operation to create a math problem. Displaying them suggests using string representations of the operators.
However, you also want to perform the operation that the operator represents; that means you have to turn that operator string into a computation in some way. One way is to switch on the chosen operator, similar to what hev1 does...
let num1 = 839;
let num2 = 195;
let op = '+';
let answer;
switch (op) {
case '*':
answer = num1 * num2;
break;
case '/':
answer = num1 / num2;
break;
case '+':
answer = num1 + num2;
break;
case '-':
answer = num1 - num2;
break;
}
console.log('answer is', answer);
But now your array and your computation code have to be kept synchronized.
You can also create a function array like 8HoLoN does, but then you have no symbol to display in the page unless you again have a corresponding way to pick both the symbol to display and the operation to perform.
I would create an array of objects, where each object contains the symbol to display and a function to perform the operation.
Look in particular at how the answer is calculated at let answer = ... and how the operator is displayed in the console.log line.
// Here's your array of operators. Each object in the array has
// • an "operator" symbol
// • a calculator to perform that operation.
const operators = [
{
'operator' : '*',
'calc' : (a, b) => a * b
},
{
'operator' : '/',
'calc' : (a, b) => a / b
},
{
'operator' : '+',
'calc' : (a, b) => a + b
},
{
'operator' : '-',
'calc' : (a, b) => a - b
},
];
let num1 = 296;
let num2 = 138;
let op = operators[getRandomIntInclusive(0, operators.length - 1)];
let answer = op.calc(num1, num2);
console.log(num1, op.operator, num2, '=', answer);
/* random int function from MDN
https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/random#Getting_a_random_integer_between_two_values_inclusive
*/
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
You simply need to add quote around the symbols as follows -
var operations = ['*', '+', '-', '/'];
console.log(operations);
function getRandom(){
return operations[Math.floor(Math.random() * Math.floor(4))]
}
// To get a random symbol -
console.log(getRandom())
// One way to use a random symbol in a given expression would be as -
const num1 = Math.floor(100*Math.random()), num2 = Math.floor(100*Math.random());
const expression = `${num1}${getRandom()}${num2}`
const result = eval(expression)
console.log(result)
As you can see, you can use eval(), if you are forming an expression string using the way I have shown in the above code snippet. The eval() function evaluates JavaScript code represented as a string.
You can't. * / + - are operators of the JS language and then you can't reference them as objects in a array.
If what you want is make dynamic random operation, you should use function.
const operators = [
(a, b) => a + b,
(a, b) => a * b,
(a, b) => a - b,
(a, b) => a / b
];
const a = 2, b = 3;
const rand = Math.floor(Math.random() * 3.9);// between [0;4[
const res = operators[rand](a, b);
console.log(res);

Using variable that contains "multiply" string for multiplication of 2 other numbers

I have a task where I should create 3 variables, 2 of them will be numbers (2,3) and the 3rd will be '*' or '/' sign string.
I need to write a function that will return the answer of 2*3 and 2/3 by using the 3 variables only for the outcome.
I have no idea how to use the variable that contains a string, and that string to perform the operation of subtract or multiply.
function multiply (a , b ,c ) {
}
console.log (multiply(4, (*) , 5 )) ;
I expect the outcome to be 4* 5 and 4/5
You're trying to pass an operator as a parameter to a function, which isn't possible in javascript. You can still make this function though, but using strings as an alternative.
function multiply(a, b, op) {
if (op === '*') return a * b;
if (op === '/') return a / b;
}
console.log(multiply(2, 5, '*')); // should return 10
console.log(multiply(2, 5, '/')); // should return 0.4
const handlers = {
'*': (a, b) => { return a * b; },
'/': (a, b) => { return a / b; },
}
function multiply (a , b ,c ) {
return handlers[b](a, c);
}
Put it on if condition and match ur sign
function multiply (a , b ,c ) {
return c === '*' ? a*b : a/b
}
console.log (multiply(4, 5, '/' )) ;
Hope it help
const multiply = (num1, num2, calc) => `${num1}${calc}${num2}`
console.log(multiply(1,2,"*"))

Concatenate operator in math operation in Javascript

I want to create a random generator of math operations. I'm trying with ASCII codes but what happens is that it just concatenate the operands and operators as a String. Anyone has a suggestion for this?
let a = Math.floor(Math.random()*100)
let b = Math.floor(Math.random()*100)
let ascCode = Math.floor(Math.random()* (46 - 42)) + 42
let op = String.fromCharCode(ascCode)
let c = a + `${op}` + b;
console.log(c)
You can use eval:
The eval() function evaluates JavaScript code represented as a string.
let a = 1
let b = 2
let ascCode = Math.floor(Math.random()* (46 - 42)) + 42
let op = String.fromCharCode(ascCode)
let c = eval(a + `${op}` + b);
console.log(c)
But eval can be troublesome.
eval() is a dangerous function, which executes the code it's passed with the privileges of the caller. If you run eval() with a string that could be affected by a malicious party, you may end up running malicious code on the user's machine with the permissions of your webpage / extension. More importantly, a third-party code can see the scope in which eval() was invoked, which can lead to possible attacks in ways to which the similar Function is not susceptible.
eval() is also slower than the alternatives, since it has to invoke the JS interpreter, while many other constructs are optimized by modern JS engines.
msdn
Another solution would be to use some if:
let a = 1
let b = 2
let ascCode = Math.floor(Math.random()* (46 - 42)) + 42
let op = String.fromCharCode(ascCode)
let c = 0;
if (op == "*")
c = a + b;
if (op == "+")
c = a + b;
if (op == "-")
c = a - b;
/*etc*/
console.log(c)
or even a map where keys are operators and values are functions:
let a = 1
let b = 2
let ascCode = Math.floor(Math.random()* (46 - 42)) + 42
let op = String.fromCharCode(ascCode)
let operators = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => a / b, // handle zero!
'%': (a, b) => a % b // handle zero!
};
console.log(operators[op](a,b))
This solution is similar to things discussed in the comments. It is a different, non-eval way of generating random math operations:
const ops = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => a / b,
'%': (a, b) => a % b,
'>': (a, b) => a > b,
'<': (a, b) => a < b,
// etc
}
const randomOperation = ((ops) => {
const keys = Object.keys(ops)
const randomOpKey = () => keys[Math.floor(Math.random() * keys.length)]
const evalOp = (key, a, b) => ops[key](a, b)
return () => {
const op = randomOpKey()
const a = Math.floor(Math.random() * 100)
const b = Math.floor(Math.random() * 100)
return {
expression: `${a} ${op} ${b}`,
result: evalOp(op, a, b)
}
}
})(ops)
// demo
for (let i = 0; i < 20; i++) {
let {expression, result} = randomOperation()
console.log(`${expression} = ${result}`)
}
Note that what randomOperation returns is objects with two properties: expression as a string, and result as a value, which will be numeric or boolean. The demo code shows one way to use it. You have to manually maintain the list of operations, which is different than with the eval solutions.
I don't know what better suits your needs, but this should show that there are reasonable non-eval solutions possible.
What you could use (though this is definitely not a good practice), is eval(), which evaluates a string as javascript code.
(Another word of warning, eval is evil and shouldn't be used in production)
let a = Math.floor(Math.random()*100)
let b = Math.floor(Math.random()*100)
let ascCode = Math.floor(Math.random()* (46 - 42)) + 42
let op = String.fromCharCode(ascCode)
let c = eval(a + `${op}` + b);
console.log(c)
You should use built-in function eval()
let operators = ['+','-','*','/','%'];
let number1 = 5;
let number2 = 4;
function getRandomOperation(){
let randOpeator = operators[Math.floor(Math.random() * operators.length)];
return eval(`${number1} ${randOpeator} ${number2}`);
}
console.log(getRandomOperation());
You should evalute expression, using javascript eval function. It expects string as input.
const result = eval(c);
console.log(result);
Reference
I presume it happens because you are using concatenation when one from concatenated values is a string:
let op = String.fromCharCode(ascCode)
To obtain a number under variable c you have to make sure that ${op} is number as well.

Categories