How do I do division with reduce function? - javascript

So I am trying to divide a list of number and here is the code for it.
var myArray = document.getElementById("listInput").value.split(" ");
I took the value from the box "listInput" and I split it so it's easier to divide. Then:
for(i=0;i<myArray.length; i+=1){
if(myArray[i] === "divide")
Up here I made a for loop so the program would go through each splitted word until it finds the string "divide." After that:
{
document.getElementById("resultNumberTotal").value =
myArray.reduce(function(total, curr) {
if (!isNaN(Number(curr))) {
total = Number(curr) /total ;
}
return total;
}, 1);
}
}
}
Up in this code, the box where I want to show the result "resultNumberTotal", I made it equal to a ".reduce function" as u can. I recently learnt .reduce is a good way to add/multiply/divide so. I have 2 parameters. The program goes through the loop, finds a number(curr) then finds the command "divide" then finds another number called "total and evaluated the answer for example:
[9 divide 3] = 9/3 = 3 or [1 divide 10] = 1/10 = 1
What am I doing wrong. Plz help me understand as well Thank you

Here's an example that uses a regular expression to extract the different parts of the calculation from the string in the input element.
One of the things it extracts is the operation which needs to be done and our function uses this value to perform the calculation and return the result.
I'm not sure this is exactly what you need, but I think this is one way to solve your problem.
const input = document.getElementById("listInput");
const output = document.getElementById("resultNumberTotal");
const regex = /(\d+)\s+(divide|add|multiply|subtract)\s+(\d+)/g;
input.onchange = function(ev) {
output.value = check(input.value);
}
function check(str) {
let m = regex.exec(str);
if (m == null || m.length < 4) {
return ''
}
const a = m[1],
b = m[3],
op = m[2];
let res = NaN;
switch (op) {
case 'divide':
res = a / b;
break;
case 'add':
res = a + b;
break;
case 'multiply':
res = a * b;
break;
case 'subtract':
res = a - b;
break;
}
return '[' + a + ' ' + op + ' ' + b + '] = ' + res
}
<input type="text" id="listInput" />
<input type="text" id="resultNumberTotal" />

Related

How do get input 2^3 to Math.pow(2, 3)?

I have this simple calculator script, but it doesn't allow power ^.
function getValues() {
var input = document.getElementById('value').value;
document.getElementById('result').innerHTML = eval(input);
}
<label for="value">Enter: </label><input id="value">
<div id="result">Results</div>
<button onclick="getValues()">Get Results</button>
I tried using input = input.replace( '^', 'Math.pow(,)');
But I do not know how to get the values before '^' and after into the brackets.
Example: (1+2)^3^3 should give 7,625,597,484,987
Use a regular expression with capture groups:
input = '3 + 2 ^3';
input = input.replace(/(\d+)\s*\^\s*(\d+)/g, 'Math.pow($1, $2)');
console.log(input);
This will only work when the arguments are just numbers. It won't work with sub-expressions or when you repeat it, like
(1+2)^3^3
This will require writing a recursive-descent parser, and that's far more work than I'm willing to put into an answer here. Get a textbook on compiler design to learn how to do this.
I don't think you'll be able to do this with simple replace.
If you want to parse infix operators, you build two stacks, one for symbols, other for numbers. Then sequentially walk the formula ignoring everything else than symbols, numbers and closing parenthesis. Put symbols and numbers into their stacks, but when you encounter closing paren, take last symbol and apply it to two last numbers. (was invented by Dijkstra, I think)
const formula = '(1+2)^3^3'
const symbols = []
const numbers = []
function apply(n2, n1, s) {
if (s === '^') {
return Math.pow(parseInt(n1, 10), parseInt(n2, 10))
}
return eval(`${n1} ${s} ${n2}`)
}
const applyLast = () => apply(numbers.pop(), numbers.pop(), symbols.pop())
const tokenize = formula => formula.split(/(\d+)|([\^\/\)\(+\-\*])/).filter(t => t !== undefined && t !== '')
const solver = (formula) => {
const tf = tokenize(formula)
for (let l of formula) {
const parsedL = parseInt(l, 10)
if (isNaN(parsedL)) {
if (l === ')') {
numbers.push(applyLast())
continue
} else {
if (~['+', '-', '*', '/', '^'].indexOf(l))
symbols.push(l)
continue
}
}
numbers.push(l)
}
while (symbols.length > 0)
numbers.push(applyLast())
return numbers.pop()
}
console.log(solver(formula))
Get your input into a string and do...
var input = document.getElementById('value').value;
var values = input.split('^'); //will save an array with [value1, value 2]
var result = Math.pow(values[0], values[1]);
console.log(result);
This only if your only operation is a '^'
EDIT: Saw example after edit, this no longer works.
function getValues() {
var input = document.getElementById('value').value;
// code to make ^ work like Math.pow
input = input.replace( '^', '**');
document.getElementById('result').innerHTML = eval(input);
}
The ** operator can replace the Math.pow function in most modern browsers. The next version of Safari (v10.1) coming out any day supports it.
As said in other answers here, you need a real parser to solve this correctly. A regex will solve simple cases, but for nested statements you need a recursive parser. For Javascript one library that offers this is peg.js.
In your case, the example given in the online version can be quickly extended to handle powers:
Expression
= head:Term tail:(_ ("+" / "-") _ Term)* {
var result = head, i;
for (i = 0; i < tail.length; i++) {
if (tail[i][1] === "+") { result += tail[i][3]; }
if (tail[i][1] === "-") { result -= tail[i][3]; }
}
return result;
}
Term
= head:Pow tail:(_ ("*" / "/") _ Pow)* { // Here I replaced Factor with Pow
var result = head, i;
for (i = 0; i < tail.length; i++) {
if (tail[i][1] === "*") { result *= tail[i][3]; }
if (tail[i][1] === "/") { result /= tail[i][3]; }
}
return result;
}
// This is the new part I added
Pow
= head:Factor tail:(_ "^" _ Factor)* {
var result = 1;
for (var i = tail.length - 1; 0 <= i; i--) {
result = Math.pow(tail[i][3], result);
}
return Math.pow(head, result);
}
Factor
= "(" _ expr:Expression _ ")" { return expr; }
/ Integer
Integer "integer"
= [0-9]+ { return parseInt(text(), 10); }
_ "whitespace"
= [ \t\n\r]*
It returns the expected output 7625597484987 for the input string (1+2)^3^3.
Here is a Python-based version of this question, with solution using pyparsing: changing ** operator to power function using parsing?

Increment numbers at end of alphanumeric string in JavaScript

I have alphanumeric strings that will always end in a number, but which may have other numbers embedded early on.
I need to increment the numeric ending and return new ID numbers.
Example:
A48-DBD7-398
Which will be incremented in a loop:
A48-DBD7-398
A48-DBD7-399
A48-DBD7-400
How do I separate out the numeric tail from the rest of the string, and then save the two parts into different variables?
I found several other S.O. questions that split numbers out of a string, but they cannot handle mixed alphanumeric characters in the first part -- or else they split out ALL the numbers, regardless where they are. I need to get only the trailing digits.
Update
I found a case where my solution does not work:
ABC123-DE45-1
Duplicates as:
ABC2
ABC3
ABC4
JS Fiddle demo
If you are interested in a different approach you could do something like this:
$('button').click(function () {
var value = $('#in').val(); // get value
for (var i = 1; i <= 5; i++) {
value = value.replace(/(\d+)$/, function (match, n) {
return ++n; // parse to int and increment number
}); // replace using pattern
$('#result')[0].innerHTML += '<br>' + value;
}
});
My 2 cents: use regex to identify the pattern and increment the last part.
function incrementAlphanumeric(str) {
const numPart = str.match(/(0?[1-9])+$|0?([1-9]+?0+)$/)[0];
const strPart = str.slice(0, str.indexOf(numPart));
const isLastIndexNine = numPart.match(/9$/);
// If we have a leading zero (e.g. - 'L100A099')
// or there is no prefix - we should just increment the number
if (isLastIndexNine || strPart != null) {
return strPart + numPart.replace(/\d+$/, (n) => ++n );
}
// Increment the number and add the missing zero
else {
return strPart + '0' + numPart.replace(/\d+$/, (n) => ++n );
}
}
works with the following formats for example:
TEST01A06
TEST-100-A100
TEST0001B-101
TEST001A100
TEST001A91
TEST1101
TEST1010
1010
Demo Repl - https://repl.it/#EdoMagen/Increment-alphanumeric-string
Here is another solution, in case it helps
$('button').click(function() {
var ser = $('#in').val();
var arr = ser.split("-");
var num = parseInt(arr[arr.length - 1]);
arr.splice(-1, 1);
var str = arr.join ('-');
for (n = 1; n <= 5; n++) {
num++;
ser = str + '-' + num;
$('#result').html($('#result').html() + '<br>' + ser);
}
});
div{width:80%;margin-top:30px;background:wheat;}
<input id="in" type="text" value="ABC123-DE45-1" />
<button>Go</button>
<div id="result"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I figured it out, and am posting the question for future seekers.
JS Fiddle demo
HTML:
<input id="in" type="text" value="A48-DBD7-395" />
<button>Go</button>
<div id="result"></div>
js/jQ:
$('button').click(function(){
var ser = $('#in').val();
var num = parseInt(ser.match(/\d+$/));
var pos = ser.indexOf(num);
var str = ser.slice(0,pos);
for (n=1;n<=5;n++){
num++;
ser = str + num;
$('#result').html( $('#result').html() +'<br>'+ser);
}
});
const s = "A48-DBD7-398";
s.split('-').reduce((a,b)=>{
if(Number(b)){b = Number(b) + 1}
return a +'-'+ b;
})
> "A48-DBD7-399"

Adding Pronumerals together in javascript

I want javascript to be able to interpret the following (a and b are always going to be different, so these are just an example)
a=(3x)+y
b=x+(4y)
and return the following
a+b=(4x)+(5y)
all variables are strings and not integers so math can not be applied to a,b,x or y
I have not started on this particular instance, due to the fact that i don't know where to start.
P.S. I have not had any experience with jQuery, so if possible, try and avoid it
EDIT: The program is designed to help find raw materials in the game minecraft. For example if you want a diamond sword (a) and a diamond pickaxe (b), a requires 1 wood (x) and 2 diamonds (y), and b requires 1 wood (x) and 3 diamonds (y). Once i run it through this program, i would like a response saying that it requires 2 wood and 5 diamonds. Sorry for any prior confusion...
First, let's program three little helper functions:
// exprToDict("3x + y") -> {x:3, y:1}
function exprToDict(e) {
var d = {};
e.replace(/(\d+)\s*(\w+)|(\w+)/g, function($0, $1, $2, $3) {
d[$2 || $3] = parseInt($1 || 1);
});
return d;
}
// addDicts({x:1, y:2}, {x:100, y:3}) -> {x:101, y:5}
function addDicts(a, b) {
var d = {};
for(var x in a) d[x] = a[x];
for(var x in b) d[x] = (d[x] || 0) + b[x];
return d;
}
// dictToExpr({x:1, y:2}) -> x + (2 y)
function dictToExpr(d) {
var e = [];
for(var x in d)
if(d[x] == 1)
e.push(x);
else
e.push("(" + d[x] + " " + x + ")");
return e.join(" + ")
}
Once we've got that, we're ready to code the main function:
function addThings(a, b) {
return dictToExpr(
addDicts(
exprToDict(a),
exprToDict(b)
))
}
Let's test it:
sword = "(3 wood) + diamond"
pickaxe = "wood + (2 diamond)"
console.log(addThings(sword, pickaxe))
Result:
(4 wood) + (3 diamond)
In order to process more than two things, modify addDicts to accept arrays:
function addDicts(dicts) {
var sum = {};
dicts.forEach(function(d) {
for(var x in d)
sum[x] = (sum[x] || 0) + d[x];
});
return sum;
}
and rewrite addThings to be:
function addThings(things) {
return dictToExpr(
addDicts(
things.map(exprToDict)));
}
Example:
sword = "(3 wood) + diamond"
pickaxe = "wood + (2 diamond)"
house = "10 wood + iron"
console.log(addThings([sword, pickaxe, house]))
First, parse the input string - according to your grammar - to an object to work with:
function parseLine(input) { // pass a string like "a=(3x)+y"
var parts = input.split("=");
if (parts.length != 2) return alert("Invalid equation");
for (var i=0; i<2; i++) {
var summands = parts[i].split("+");
parts[i] = {};
for (var j=0; j<summands.length; j++) {
summands[j] = summands[j].replace(/^\s*\(?|\)?\s*$/g, "");
var match = summands[j].match(/^(-?\d*\.?\d+)?\s*([a-z]+)$/);
if (!match) return alert("Parse error: "+summands[i]);
var mul = parseFloat(match[1] || 1);
if (match[2] in parts[i])
parts[i][match[2]] += mul;
else
parts[i][match[2]] = mul;
}
}
return parts;
}
// example:
parseLine("a=(3x)+y")
// [{"a":1},{"x":3,"y":1}]
Then, apply an algorithm for solving linear equation systems on it. I leave the implementation of that to you :-)
Wow, you're question has so radically changed that I'll write a completely new answer:
You can just use objects for that. Store the materials needed for the tools in key-value-maps:
var diamondSword = {
diamond: 2,
stick: 1
};
var diamondPickaxe = {
diamond: 3,
stick: 2
};
An addition function is very simple then:
function add() {
var result = {};
for (var i=0; i<arguments.length; i++)
for (var item in arguments[i])
result[item] = (result[item] || 0) + arguments[i][item];
return result;
}
// usage:
add(diamondSword, diamondPickaxe)
// {"diamond":5, "stick":3}

Javascript multiple digit index

I have searched around the net and the solution must be so simple no one has asked?
I just wanted to use an index like + i + to return 001, 002, 003, etc
How about
('000' + i).substr(-3);
So something like this?
function number_pad(num,len) {
num = ""+num;
while(num.length < len) num = "0"+num;
return num;
}
// Usage: number_pad(i,3);
Alternatively, extend the native object:
Number.prototype.pad(len) {
var num = ""+this;
while(num.length < len) num = "0"+num;
return num;
}
// Usage: i.pad(3);
For future reference, this is called zerofill or zero-padding.
function paddedNumber(n) {
// A string containing the fully padded zero value.
var zeroes = "000";
// The number as a string.
var numstr = "" + n;
var nDigits = numstr.length;
// Keep any sign at the front.
var sign = "";
if (/^[\+\-]/.test(numstr)) {
sign = numstr.charAt(0);
numstr = numstr.substring(1);
}
// Concatenates the number with just enough zeroes.
// No padding if itoa is already longer than the pad.
return sign + zeroes.substring(nDigits) + numstr;
}

Calculate string value in javascript, not using eval

Is there a way to calculate a formula stored in a string in JavaScript without using eval()?
Normally I would do something like
var apa = "12/5*9+9.4*2";
console.log(eval(apa));
So, does anyone know about alternatives to eval()?
Mhh, you could use the Function() constructor:
function evil(fn) {
return new Function('return ' + fn)();
}
console.log(evil('12/5*9+9.4*2')); // => 40.4
There's nothing wrong with eval, especially for cases like this. You can sanitize the string with a regex first to be safe:
// strip anything other than digits, (), -+/* and .
var str = "12/5*9+9.4*2".replace(/[^-()\d/*+.]/g, '');
console.log(eval(str));
Eval was built for conditions like this.
If you wanted another method, you'd have to use a pure Javascript implementation of the exact thing eval is going to do.
The hard part is not the parsing of numbers and operators
The hard part is applying order of operation and recursive control
Here's a quick basic example I came up with (updated (2011-06-26): cleaner w/ input boxes).
http://jsfiddle.net/vol7ron/6cdfA/
Note:
it only handles the basic operators
it does not check the validity of the numbers (example: divide by zero)
it has not implemented parenthetical operation
for all these reasons and more, eval would be a better choice
Edit (2017-05-26) to use SO Snippet:
function calculate(input) {
var f = {
add: '+',
sub: '-',
div: '/',
mlt: '*',
mod: '%',
exp: '^'
};
// Create array for Order of Operation and precedence
f.ooo = [
[
[f.mlt],
[f.div],
[f.mod],
[f.exp]
],
[
[f.add],
[f.sub]
]
];
input = input.replace(/[^0-9%^*\/()\-+.]/g, ''); // clean up unnecessary characters
var output;
for (var i = 0, n = f.ooo.length; i < n; i++) {
// Regular Expression to look for operators between floating numbers or integers
var re = new RegExp('(\\d+\\.?\\d*)([\\' + f.ooo[i].join('\\') + '])(\\d+\\.?\\d*)');
re.lastIndex = 0; // take precautions and reset re starting pos
// Loop while there is still calculation for level of precedence
while (re.test(input)) {
output = _calculate(RegExp.$1, RegExp.$2, RegExp.$3);
if (isNaN(output) || !isFinite(output))
return output; // exit early if not a number
input = input.replace(re, output);
}
}
return output;
function _calculate(a, op, b) {
a = a * 1;
b = b * 1;
switch (op) {
case f.add:
return a + b;
break;
case f.sub:
return a - b;
break;
case f.div:
return a / b;
break;
case f.mlt:
return a * b;
break;
case f.mod:
return a % b;
break;
case f.exp:
return Math.pow(a, b);
break;
default:
null;
}
}
}
label {
display: inline-block;
width: 4em;
}
<div>
<label for="input">Equation: </label>
<input type="text" id="input" value="12/5*9+9.4*2-1" />
<input type="button"
value="calculate"
onclick="getElementById('result').value = calculate(getElementById('input').value)" />
</div>
<div>
<label for="result">Result: </label>
<input type="text" id="result" />
</div>
This is exactly the place where you should be using eval(), or you will have to loop through the string and generate the numbers. You will have to use the Number.isNaN() method to do it.
Here is an implementation of the Shunting-yard algorithm with additional support for unary prefix (e.g. -) and postfix (e.g. !) operators, and function (e.g. sqrt()) notations. More operators/functions can be easily defined with the Calculation.defineOperator method:
"use strict";
class Calculation {
constructor() {
this._symbols = {};
this.defineOperator("!", this.factorial, "postfix", 6);
this.defineOperator("^", Math.pow, "infix", 5, true);
this.defineOperator("*", this.multiplication, "infix", 4);
this.defineOperator("/", this.division, "infix", 4);
this.defineOperator("+", this.last, "prefix", 3);
this.defineOperator("-", this.negation, "prefix", 3);
this.defineOperator("+", this.addition, "infix", 2);
this.defineOperator("-", this.subtraction, "infix", 2);
this.defineOperator(",", Array.of, "infix", 1);
this.defineOperator("(", this.last, "prefix");
this.defineOperator(")", null, "postfix");
this.defineOperator("min", Math.min);
this.defineOperator("sqrt", Math.sqrt);
}
// Method allowing to extend an instance with more operators and functions:
defineOperator(symbol, f, notation = "func", precedence = 0, rightToLeft = false) {
// Store operators keyed by their symbol/name. Some symbols may represent
// different usages: e.g. "-" can be unary or binary, so they are also
// keyed by their notation (prefix, infix, postfix, func):
if (notation === "func") precedence = 0;
this._symbols[symbol] = Object.assign({}, this._symbols[symbol], {
[notation]: {
symbol, f, notation, precedence, rightToLeft,
argCount: 1 + (notation === "infix")
},
symbol,
regSymbol: symbol.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&')
+ (/\w$/.test(symbol) ? "\\b" : "") // add a break if it's a name
});
}
last(...a) { return a[a.length-1] }
negation(a) { return -a }
addition(a, b) { return a + b }
subtraction(a, b) { return a - b }
multiplication(a, b) { return a * b }
division(a, b) { return a / b }
factorial(a) {
if (a%1 || !(+a>=0)) return NaN
if (a > 170) return Infinity;
let b = 1;
while (a > 1) b *= a--;
return b;
}
calculate(expression) {
let match;
const values = [],
operators = [this._symbols["("].prefix],
exec = _ => {
let op = operators.pop();
values.push(op.f(...[].concat(...values.splice(-op.argCount))));
return op.precedence;
},
error = msg => {
let notation = match ? match.index : expression.length;
return `${msg} at ${notation}:\n${expression}\n${' '.repeat(notation)}^`;
},
pattern = new RegExp(
// Pattern for numbers
"\\d+(?:\\.\\d+)?|"
// ...and patterns for individual operators/function names
+ Object.values(this._symbols)
// longer symbols should be listed first
.sort( (a, b) => b.symbol.length - a.symbol.length )
.map( val => val.regSymbol ).join('|')
+ "|(\\S)", "g"
);
let afterValue = false;
pattern.lastIndex = 0; // Reset regular expression object
do {
match = pattern.exec(expression);
const [token, bad] = match || [")", undefined],
notNumber = this._symbols[token],
notNewValue = notNumber && !notNumber.prefix && !notNumber.func,
notAfterValue = !notNumber || !notNumber.postfix && !notNumber.infix;
// Check for syntax errors:
if (bad || (afterValue ? notAfterValue : notNewValue)) return error("Syntax error");
if (afterValue) {
// We either have an infix or postfix operator (they should be mutually exclusive)
const curr = notNumber.postfix || notNumber.infix;
do {
const prev = operators[operators.length-1];
if (((curr.precedence - prev.precedence) || prev.rightToLeft) > 0) break;
// Apply previous operator, since it has precedence over current one
} while (exec()); // Exit loop after executing an opening parenthesis or function
afterValue = curr.notation === "postfix";
if (curr.symbol !== ")") {
operators.push(curr);
// Postfix always has precedence over any operator that follows after it
if (afterValue) exec();
}
} else if (notNumber) { // prefix operator or function
operators.push(notNumber.prefix || notNumber.func);
if (notNumber.func) { // Require an opening parenthesis
match = pattern.exec(expression);
if (!match || match[0] !== "(") return error("Function needs parentheses")
}
} else { // number
values.push(+token);
afterValue = true;
}
} while (match && operators.length);
return operators.length ? error("Missing closing parenthesis")
: match ? error("Too many closing parentheses")
: values.pop() // All done!
}
}
Calculation = new Calculation(); // Create a singleton
// I/O handling
function perform() {
const expr = document.getElementById('expr').value,
result = Calculation.calculate(expr);
document.getElementById('out').textContent = isNaN(result) ? result : '=' + result;
}
document.getElementById('expr').addEventListener('input', perform);
perform();
// Tests
const tests = [
{ expr: '1+2', expected: 3 },
{ expr: '1+2*3', expected: 7 },
{ expr: '1+2*3^2', expected: 19 },
{ expr: '1+2*2^3^2', expected: 1025 },
{ expr: '-3!', expected: -6 },
{ expr: '12---11+1-3', expected: -1 },
{ expr: 'min(2,1,3)', expected: 1 },
{ expr: '(2,1,3)', expected: 3 },
{ expr: '4-min(sqrt(2+2*7),9,5)', expected: 0 },
{ expr: '2,3,10', expected: 10 }
]
for (let {expr, expected} of tests) {
let result = Calculation.calculate(expr);
console.assert(result === expected, `${expr} should be ${expected}, but gives ${result}`);
}
#expr { width: 100%; font-family: monospace }
Expression: <input id="expr" value="min(-1,0)+((sqrt(16)+(-4+7)!*---4)/2)^2^3"><p>
<pre id="out"></pre>
If you don't want to use eval you will have to use an existing expression evaluator library.
http://silentmatt.com/javascript-expression-evaluator/
http://www.codeproject.com/KB/scripting/jsexpressioneval.aspx
You can also roll one of your own :)
I spent a couple of hours to implement all the arithmetical rules without using eval() and finally I published a package on npm string-math. Everything is in the description. Enjoy
This solution also clips whitespaces and checks for duplicating operators
e.g. ' 1+ 2 *2' // 5 but ' 1 + +2* 2 ' // Error
function calcMe(str) {
const noWsStr = str.replace(/\s/g, '');
const operators = noWsStr.replace(/[\d.,]/g, '').split('');
const operands = noWsStr.replace(/[+/%*-]/g, ' ')
.replace(/\,/g, '.')
.split(' ')
.map(parseFloat)
.filter(it => it);
if (operators.length >= operands.length){
throw new Error('Operators qty must be lesser than operands qty')
};
while (operators.includes('*')) {
let opIndex = operators.indexOf('*');
operands.splice(opIndex, 2, operands[opIndex] * operands[opIndex + 1]);
operators.splice(opIndex, 1);
};
while (operators.includes('/')) {
let opIndex = operators.indexOf('/');
operands.splice(opIndex, 2, operands[opIndex] / operands[opIndex + 1]);
operators.splice(opIndex, 1);
};
while (operators.includes('%')) {
let opIndex = operators.indexOf('%');
operands.splice(opIndex, 2, operands[opIndex] % operands[opIndex + 1]);
operators.splice(opIndex, 1);
};
let result = operands[0];
for (let i = 0; i < operators.length; i++) {
operators[i] === '+' ? (result += operands[i + 1]) : (result -= operands[i + 1])
}
return result
}
This shows to be more performant than #vol7ron's solution.
Check this JSBenchmark
If you're looking for a syntactical equivalent to eval, you could use new Function. There are slight differences regarding scoping, but they mostly behave the same, including exposure to much of the same security risks:
let str = "12/5*9+9.4*2"
let res1 = eval(str)
console.log('res1:', res1)
let res2 = (new Function('return '+str)())
console.log('res2:', res2)
You can't, at most you could do something retort like parsing the numbers and then separating the operations with a switch, and making them. Other than that, I'd use eval in this case.
That would be something like (a real implementation will be somewhat more complex, especially if you consider the use of parenthesis, but you get the idea)
function operate(text) {
var values = text.split("+");
return parseInt(values[0]) + parseInt(values[1]);
}
console.log(operate("9+2"));
Still, I think the best choice you can make is to use eval, given that you're able to trust the source of the string.
There is also an open source implementation on GitHub, evaluator.js, and an NPM package.
From the README:
Evaluator.js is a small, zero-dependency module for evaluating mathematical expressions.
All major operations, constants, and methods are supported. Additionally, Evaluator.js intelligently reports invalid syntax, such as a misused operator, missing operand, or mismatched parentheses.
Evaluator.js is used by a desktop calculator application of the same name. See a live demo on the website.
Note : There is no library used in this solution purely hard coded
My solution takes into account of brackets also like 8+6(7(-1)) or 8+6(7(-1))
You can do these operations ^, *, /, +, -
To calculate a string use calculate(tokenize(pieval("8+6(7(-1))").join("")))
function tokenize(s) {
// --- Parse a calculation string into an array of numbers and operators
const r = [];
let token = '';
for (const character of s) {
if ('^*/+-'.indexOf(character) > -1) {
if (token === '' && character === '-') {
token = '-';
} else {
r.push(parseFloat(token), character);
token = '';
}
} else {
token += character;
}
}
if (token !== '') {
r.push(parseFloat(token));
}
return r;
}
function calculate(tokens) {
// --- Perform a calculation expressed as an array of operators and numbers
const operatorPrecedence = [{'^': (a, b) => Math.pow(a, b)},
{'*': (a, b) => a * b, '/': (a, b) => a / b},
{'+': (a, b) => a + b, '-': (a, b) => a - b}];
let operator;
for (const operators of operatorPrecedence) {
const newTokens = [];
for (const token of tokens) {
if (token in operators) {
operator = operators[token];
} else if (operator) {
newTokens[newTokens.length - 1] =
operator(newTokens[newTokens.length - 1], token);
operator = null;
} else {
newTokens.push(token);
}
}
tokens = newTokens;
}
if (tokens.length > 1) {
console.log('Error: unable to resolve calculation');
return tokens;
} else {
return tokens[0];
}
}
function pieval(input) {
let openParenCount = 0;
let myOpenParenIndex = 0;
let myEndParenIndex = 0;
const result = [];
for (let i = 0; i < input.length; i++) {
if (input[i] === "(") {
if (openParenCount === 0) {
myOpenParenIndex = i;
// checking if anything exists before this set of parentheses
if (i !== myEndParenIndex) {
if(!isNaN(input[i-1])){
result.push(input.substring(myEndParenIndex, i) + "*");
}else{
result.push(input.substring(myEndParenIndex, i));
}
}
}
openParenCount++;
}
if (input[i] === ")") {
openParenCount--;
if (openParenCount === 0) {
myEndParenIndex = i + 1;
// recurse the contents of the parentheses to search for nested ones
result.push(pieval(input.substring(myOpenParenIndex + 1, i)));
}
}
}
// capture anything after the last parentheses
if (input.length > myEndParenIndex) {
result.push(input.substring(myEndParenIndex, input.length));
}
//console.log(cal(result))
let response = cal(result);
return result;
}
function cal(arr) {
let calstr = "";
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] != "string") {
if (cal(arr[i]) < 0) {
arr[i] = `${cal(arr[i])}`;
} else {
arr[i] = `${cal(arr[i])}`;
}
}
if (typeof arr[i] === "string") {
calstr += arr[i];
}
if (i == arr.length - 1) {
//console.log("cal" ,calstr,calculate(tokenize(calstr)) );
return calculate(tokenize(calstr));
}
}
}
console.log(calculate(tokenize(pieval("8+6(7(-1))").join("")))); // ["1+",["2-",["3+4"]]]
console.log(calculate(tokenize(pieval("1+(1+(2(4/4))+4)").join("")))); // ["1+",["2-",["3+4"]]]

Categories

Resources