I am trying to create a custom formula evaluator in JavaScript that allows for nested functions. I have been able to create basic calculation functions such as AVERAGE, SUM, and MIN, (amongst many other functions) but I am having trouble with nested functions and I could use some help.
The desired behavior is as follows:
The user enters a formula into a text input, using the syntax funcName(arg1, arg2, ...), where funcName is the name of the calculation function, and arg1, arg2, ... are the arguments to the function. Arguments can either be numbers or other functions (i.e. AVERAGE(1,2) or AVERAGE(SUM(1,2),2). I would like the formulas to have an arbitrarily complex form so long as they are coded in appropriate syntax.
The user presses a button to evaluate the formula. The result of the calculation is displayed on the screen. Here's another example of what a formula might look like: AVERAGE(SUM(1, 2), 4). This formula should evaluate as follows:
The SUM(1, 2) function is evaluated first and returns 3. The AVERAGE(3, 4) function and therefore the final output, is evaluated next and returns 3.5. So far, I have been able to create the basic calculation functions such as sum and min, but I am having trouble with the nested functions. I would greatly appreciate any help or guidance that anyone can offer.
Thank you in advance!
Below is one of several different attempts I have made;
function sum(...args) {
return args.reduce((acc, arg) => {
if (isNaN(arg)) {
return acc;
}
return acc + arg;
}, 0);
}
function min(...args) {
return Math.min(...args);
}
function avg(...args) {
if (args.length === 1 && typeof args[0] === 'number') {
return args[0];
}
return sum(...args) / args.length;
}
function evaluate(formula) {
let match = /^(\w+)\((.*)\)$/.exec(formula);
if (!match) {
return parseFloat(formula);
}
let [, func, argsStr] = match;
let args = argsStr.split(',').map(arg => evaluate(arg.trim()));
switch (func) {
case 'SUM':
return sum(...args);
case 'MIN':
return min(...args);
case 'AVERAGE':
return avg(...args);
default:
return NaN;
}
}
let formulaInput = document.getElementById('formula');
let evalButton = document.getElementById('eval-btn');
let result = document.getElementById('result');
evalButton.addEventListener('click', () => {
let formula = formulaInput.value;
result.innerHTML = evaluate(formula);
});
I've been toying around with various approaches, and so far have found some success with this approach, but I don't believe this is the best path forward. For instance if I don't include the "if (isNaN(arg))" or some other variation of this, in the console I get: {Array(3): 0:NaN 1:3 2:3} for the first argument, when trying to create a complex formula like: SUM(AVERAGE(1,3), 3) with an output of NaN.