This is a basic calculator function. It only does addition, subtraction, multiplication, division and parentheses.
I'm having an infinite loop on recursive call in while section which is checking the string whether has a parentheses or not. If i would've send a string with 2 or more parentheses to the function, it has been stucking in loop. I couldn't figure out what I have done wrong.
const operators = ['+', '-', '*', '/', '(', ')'];
const numbers = [];
let arr;
const array = [];
function Calculate(string) {
string.split('').map(char => {
if (!operators.includes(char)) {
numbers.push(char);
} else {
arr = numbers.join('');
numbers.splice(0);
array.push(arr, char);
}
});
arr = numbers.join('');
const str = array.filter(char => char !== '').concat(arr);
while (str.includes('(')) { // Checking parentheses
let indexOpen = str.findIndex(char => char === '(');
let indexClose = indexOpen + 1;
let count = 1;
while (count !== 0) {
if (str[indexClose] === '(') {
count++;
} else if (str[indexClose] === ')') {
count--;
}
indexClose++;
}
if (
!operators.includes(str[indexOpen - 1])
// str[indexOpen - 1] !== '+' &&
// str[indexOpen - 1] !== '-' &&
// str[indexOpen - 1] !== '*' &&
// str[indexOpen - 1] !== '/'
) {
str.splice(indexOpen, 0, '*');
indexOpen++;
indexClose++;
}
const strPara = str
.filter((_, i) => i < indexClose - 1 && i > indexOpen)
.join('');
str.splice(indexOpen, indexClose - indexOpen, Calculate(strPara));
}
let indexMul; // Multiplication and division
let indexDiv;
while (str.includes('*') || str.includes('/')) {
indexMul =
str.findIndex(char => char === '*') === -1
? str.length
: str.findIndex(char => char === '*');
indexDiv =
str.findIndex(char => char === '/') === -1
? str.length
: str.findIndex(char => char === '/');
if (indexMul < indexDiv) {
str.splice(indexMul - 1, 3, +str[indexMul - 1] * +str[indexMul + 1] + '');
} else if (indexDiv < indexMul) {
str.splice(
indexDiv - 1,
3,
Math.trunc(+str[indexDiv - 1] / +str[indexDiv + 1]) + ''
);
}
}
while (str.length !== 1) { // Addition and subtraction
if (str[1] === '+') {
str.splice(0, 3, +str[0] + +str[2] + '');
}
if (str[1] === '-') {
str.splice(0, 3, +str[0] - +str[2] + '');
}
}
return str;
}
console.log(Calculate('3(16-(10-4)+2)/2(4*2)+1'));
I did this.
Be careful, this code does the calculations one after the other without managing operators precedence
The recursive way :
let strCalc = '3(16-(10-4)+2)/2(4*2)+1'
document.write(`${ strCalc } = ${ Calculate(strCalc) }`)
document.write(`<br><br>`)
strCalc = '5 ( -18.4 + 2 ) / ( -2 ( 4 * -.5 )) + 1'
document.write(`${ strCalc } = ${ Calculate(strCalc) }`)
function Calculate(str)
{
const ops = str
.match(/[\(\)\+\-\*\/]|((\d+\.?\d*)|(\.\d+))/g)
.reduce((r,v)=>
{
if (isNaN(v))
{
if (!r.opPrev || v==='(')
{
r.res.push(v)
r.opPrev = ( v!==')' )
}
else if (v==='-') // check negatives values
r.sign='-';
}
else
{
r.res.push(Number(`${r.sign}${v}`))
r.sign = ''
r.opPrev = false
}
return r
}, { res:[], opPrev:true, sign:'' }).res
;
let Pos = 0
;
return makeCalc()
;
function makeCalc()
{
let result = 0
, operand = '+'
, val
;
while ( Pos < ops.length && ops[Pos] !== ')' )
{
val = ops[Pos]
;
if ( val === '(' )
{
operand ??= '*' // nullish assignement operator
Pos++
val = makeCalc() // recurvive call
}
if ( isNaN(val) )
operand = val
;
else
{
switch (operand)
{
case '+' : result += val; break;
case '-' : result -= val; break;
case '/' : result /= val; break; // may do 0 divide!
case '*' : result *= val; break;
}
operand = null
}
Pos++
}
return result
}
}
Related
This question already has answers here:
Calculate string value in javascript, not using eval
(12 answers)
Closed 5 months ago.
I am trying to do a specific algorithm with Javascript, and... it's a little bit tricky. Here is what i am trying to do :
a console app (with nodejs and "readline"). I ask the user to make an operation, example :
"2 + 3 * 4 / 2 - 1"
what I want to do, is to read the string, turn it into an array, identify the priority operator (* and /), and then, replace (without regex) the operation at the place where previously there was the operation, and so on. With my example, it should do :
[2 + 12 / 2 - 1] => [2 + 6 - 1] => [8 - 1] => [7].
So far, i succeeded in making an app that can add with many operators, but I got 2 problems :
1/ I dont know how to make the priority
2/ my app does not support more than 3 operations...
Here is my code, I appreciate any helps to undrstand what I am doing wrong and any help to finally end this algo. Thank you :
const readline = require("readline-sync");
const operators = ["+", "-", "*", "/"];
const openParenthesis = ['(', '[', '{'];
const closeParenthesis = [')', ']', '}'];
var numericOperator = 0;
var operatorsList = []
var operatorArray = [];
var NumberA = 0;
var NumberB = 0;
var Total = 0;
var soloOperator = "";
var removeValFromIndex = [];
var indexOperator = [];
var opFound = "";
function askOperator() {
operator = readline.question(`make an operation: `)
operatorArray = operator.split(' ');
console.log(operatorArray, operatorArray.length)
}
askOperator();
splitArray(operatorArray);
function splitArray(sentenceArray) {
for (let i = 0; i < sentenceArray.length; i++) {
opFound = operators.find(el => el == sentenceArray[i]);
if(opFound == "*") {
const findMultiplyer = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findMultiplyer);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
} else if (opFound == "/") {
const findDivider = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findDivider);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
} else if (opFound == "+") {
const findAdd = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findAdd);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
} else if (opFound == "-") {
const findMinus = (element) => element == opFound;
indexOperator = sentenceArray.findIndex(findMinus);
soloOperator = sentenceArray[indexOperator];
NumberA = sentenceArray[indexOperator - 1];
NumberB = sentenceArray[indexOperator + 1];
removeValFromIndex.push((indexOperator - 1), indexOperator, (indexOperator + 1));
for (var j = removeValFromIndex.length -1; j >= 0; j--){
sentenceArray.splice(removeValFromIndex[j],1);
}
}
console.log("loop", opFound, "la", removeValFromIndex ,sentenceArray)
}
console.log("test", indexOperator, "other", soloOperator, NumberA, NumberB);
doMath(NumberA, NumberB)
}
function doMath(numA, numB) {
console.log("index in math", indexOperator)
switch (soloOperator) {
case '+' :
Total = (parseInt(numA) + parseInt(numB));
// operatorArray trouver * ou / si cest le cas on saute cette section
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
console.log("addition", Total, "new array", operatorArray );
break;
case '-' :
Total = numA - numB;
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
console.log("substract", Total, "new array", operatorArray);
break;
case '*' :
Total = numA * numB;
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
console.log(indexOperator,"multiply", Total, "new array", operatorArray);
break;
case '/' :
Total = numA / numB;
if (indexOperator > 1) {
operatorArray.splice((indexOperator), 0, Total.toString())
} else {
operatorArray.splice((indexOperator -1), 0, Total.toString())
}
if (operatorArray.length >= 3) {
return splitArray(operatorArray)
}
operatorArray.splice((indexOperator), 0, Total.toString())
console.log("divide", Total, "new array", operatorArray);
break;
default:
console.log("An error occured")
break;
}
}
so, I found a working solution, here it is, but still, there is a regex inside, is there any way to replace it by something else?
const readline = require("readline-sync");
var sentence = "";
function askOperator() {
sentence = readline.question(`make an operation: `);
}
askOperator();
function MathSolver() {
this.infixToPostfix = function (infix) {
var outputQueue = "";
var operatorStack = [];
var operators = {
"^": {
precedence: 4,
associativity: "Right"
},
"/": {
precedence: 3,
associativity: "Left"
},
"*": {
precedence: 3,
associativity: "Left"
},
// <<<< test
"%": {
precedence: 3,
associativity: "Left"
},
// >>>>
"+": {
precedence: 2,
associativity: "Left"
},
"-": {
precedence: 2,
associativity: "Left"
}
};
infix = infix.replace(/\s+/g, "");
infix = infix.split(/([\+\-\*\%\/\^\(\)])/).clean();
for (var i = 0; i < infix.length; i++) {
var token = infix[i];
console.log("token",token)
if (isNumeric(token)) {
outputQueue += token + " ";
} else if ("^*%/+-".indexOf(token) !== -1) {
var o1 = token;
var o2 = operatorStack[operatorStack.length - 1];
while (
"^*%/+-".indexOf(o2) !== -1 &&
((operators[o1].associativity === "Left" &&
operators[o1].precedence <= operators[o2].precedence) ||
(operators[o1].associativity === "Right" &&
operators[o1].precedence < operators[o2].precedence))
) {
outputQueue += operatorStack.pop() + " ";
o2 = operatorStack[operatorStack.length - 1];
}
operatorStack.push(o1);
} else if (token === "(") {
operatorStack.push(token);
} else if (token === ")") {
while (operatorStack[operatorStack.length - 1] !== "(") {
outputQueue += operatorStack.pop() + " ";
}
operatorStack.pop();
}
}
while (operatorStack.length > 0) {
outputQueue += operatorStack.pop() + " ";
}
return outputQueue;
};
}
let ms = new MathSolver();
console.log("ms>>", ms.infixToPostfix("10 - 15 * 20"));
const e = ms.infixToPostfix(sentence);
console.log("e>", e);
const s = [],
tokens = e.split(" ");
for (const t of tokens) {
const n = Number(t);
if (!isNaN(n)) {
s.push(n);
} else {
if (s.length < 2) {
throw new Error(`${t}: ${s}: insufficient operands.`);
}
const o2 = s.pop(),
o1 = s.pop();
switch (t) {
case "+":
s.push(o1 + o2);
break;
case "-":
s.push(o1 - o2);
break;
case "*":
s.push(o1 * o2);
break;
case "%":
s.push(o1, (o1 / 100) * o2);
break;
case "/":
s.push(o1 / o2);
break;
case "^":
s.push(Math.pow(o1, o2));
break;
default:
throw new Error(`Unrecognized operator: [${t}]`);
}
}
console.log(`${t}: ${s}`);
}
function isNumeric(num) {
return !isNaN(parseFloat(num)) && isFinite(num);
}
I'm trying to make a recursive function to print the factorial of a given integer. Ask the user to enter a positive integer and then display the output on a page. For example, if the user enters 5, the output must be
5 × 4 × 3 × 2 × 1 = 120
var integer = prompt("Enter a positive integer.");
function factorialize(num) {
if(num == 0 || num == 1) {
return 1;
}
else {
return num + " x " + factorialize(num-1) + num * factorialize(num-1);
}
}
document.write(factorialize(integer));
You can pass a runningTotal of the sum so far to each recursive call. You can also keep the solution compact using template literals.
function factorialize(n, runningTotal = 1) {
if (n === 1) return `1 = ${runningTotal}`;
return `${n} x ${factorialize(n - 1, runningTotal * n)}`;
}
console.log(factorialize(5));
You could handover the parts of product and result.
function factorialize(num, product = 1, result = '') {
return num === 0 || num === 1
? result + (result && ' x ') + num + ' -> ' + product
: factorialize(num - 1, product * num, result + (result && ' x ') + num);
}
console.log(factorialize(5));
console.log(factorialize(2));
console.log(factorialize(1));
console.log(factorialize(0));
I think make that recursively is quite confused:
function factorialize(n, expression = '', result = 0) {
if (n < 0) {
return null
} else if (n === 0) {
return (expression || n) + " = " + result
}
const newExpression = result ? expression + " x " + n : n
const newResult = !result ? n : result * n
return factorialize(n - 1, newExpression, newResult)
}
console.log(factorialize(5))
Is better to segregate the responsibilities:
function factorial(n) {
let fact = 1
if (n < 0) {
console.warn("Error");
return 0
} else {
for (let i = n; i > 0; i--) {
fact = fact * i;
}
}
return fact
}
function factorialExpression(n) {
let expression = ""
if (n < 0) {
console.warn("Error");
return ""
} else {
for (let i = n; i > 0; i--) {
expression += (i < n ? " x " : "") + i
}
}
return expression
}
function factorialize(n) {
if (n === 0 || n === 1) {
return n + " = " + n
} else if (n > 1) {
return factorialExpression(n) + " = " + factorial(n)
}
return null
}
console.log(factorialize(5))
I want to write a function that inserts dashes (' - ') between each two odd numbers and inserts asterisks (' * ') between each two even numbers. For instance:
Input: 99946
Output: 9-9-94*6
Input: 24877
Output: 2*4*87-7
My try
function dashAst (para) {
let stringArray = para.toString().split('');
let numbArray = stringArray.map(Number);
for (let i = 0; i<numbArray.length; i++) {
if (numbArray[i] %2 === 0 && numbArray[i+1] % 2 === 0) {
numbArray.splice(numbArray.indexOf(numbArray[i]), 0, '*')
}
else if (numbArray[i] %2 !== 0 && numbArray[i+1] %2 !== 0) {
numbArray.splice(numbArray.indexOf(numbArray[i]), 0, '-')
}
}
return numbArray
}
When I try to invoke the function it returns nothing. For instance, I tested the splice-command separately and it seems to be correct which makes it even more confusing to me. Thanks to everyone reading, or even helping a beginner out.
Looping through an Array that changes its length during the loop can be very messy (i needs to be adjusted every time you splice). It's easier to create a new result variable:
function dashAst(para) {
const stringArray = para.toString().split('');
const numbArray = stringArray.map(Number);
let result = "";
for (let i = 0; i < numbArray.length; i++) {
const n = numbArray[i], next = numbArray[i + 1];
result += n;
if (n % 2 == next % 2) {
result += n % 2 ? '-' : '*';
}
}
return result;
}
console.log(dashAst(99946)); // "9-9-94*6"
console.log(dashAst(24877)); // "2*4*87-7"
You could map the values by checking if the item and next item have the same modulo and take a separator which is defined by the modulo.
function dashAst(value) {
return [...value.toString()]
.map((v, i, a) => v % 2 === a[i + 1] % 2 ? v + '*-'[v % 2] : v)
.join('');
}
console.log(dashAst(99946)); // 9-9-94*6
console.log(dashAst(24877)); // 2*4*87-7
I hope this helps
var str = '24877';
function dashAst (para) {
let stringArray = para.toString().split('');
let numbArray = stringArray.map(x => parseInt(x));
console.log(numbArray);
var out=[];
for(let i = 0; i < numbArray.length; i++) {
if(numbArray[i] % 2 == 0){
out.push(numbArray[i]);
numbArray[i + 1] % 2 == 0 ? out.push('*') : 0;
}else if(numbArray[i] % 2 != 0) {
out.push(numbArray[i]);
numbArray[i + 1] != undefined ? out.push('-') : 0;
}
}
console.log(out.join(''));
return out;
}
dashAst(str);
I have two functions, one that checks if the passed in string contains consecutive ascending numbers, the other checks for descending consecutive numbers.
I'm wondering if these functions can be rewritten to be cleaner/ shorter than they are in their current state. At present, they're not DRY as logic is reused between the two.
The value passed in will always be a string of 6 characters, e.g "123456".
function ascendingNumbers(value) {
const valueArray = value.split("").map((item) => {
return parseInt(item, 10);
});
let match = 0;
if (valueArray[1] - 1 === valueArray[0]) {
match++;
}
for (let i = 0; i < valueArray.length - 1; i++) {
valueArray[i + 1] !== valueArray[i] + 1 ? null : match++;
}
if (match === 6) {
return false;
} else {
return true;
}
}
function descendingNumbers(value) {
const valueArray = value.split("").map((item) => {
return parseInt(item, 10);
});
let match = 0;
if (valueArray[1] + 1 === valueArray[0]) {
match++;
}
for (let i = 0; i < valueArray.length - 1; i++) {
valueArray[i + 1] !== valueArray[i] - 1 ? null : match++;
}
if (match === 6) {
return false;
} else {
return true;
}
}
You could use Array.map and Array.reduce to achieve this in a compact way, we'd create an ensureOrder function for this purpose.
You could also do this in an even more compact way with regex, using a positive lookahead to ensure we have 6 digits, then check for a range of optional digits.
function ensureOrder(value, length, ascending) {
const valueArray = Array.prototype.map.call(value, Number);
if (valueArray.length !== length) return false;
return !!valueArray.reduce((last, current, index) => ((last !== null) && (ascending ? current > last: current < last)) ? current: null);
}
console.log("Test ascending:")
let testAsc = ["345678", "123456", "456789", "123458", "223456", "1", "654321", "1234567", "string1"];
testAsc.forEach(input => console.log("Test for "+ input + ": ", ensureOrder(input, 6, true)))
console.log("Test descending:")
let testDesc = ["654321", "765432", "987654", "987321", "123456", "6", "9876543", "string1"];
testDesc.forEach(input => console.log("Test for "+ input + ": ", ensureOrder(input, 6, false)))
// Regex flavour of ascending function
function isAscending(value) {
return /^(?=\d{6}$)0?1?2?3?4?5?6?7?8?9?$/.test(value);
}
// Regex flavour of descending function
function isDescending(value) {
return /^(?=\d{6}$)9?8?7?6?5?4?3?2?1?0?$/.test(value);
}
console.log("Test ascending (regex):")
testAsc.forEach(input => console.log("Test for "+ input + ": ", isAscending(input)))
console.log("Test descending (regex):")
testDesc.forEach(input => console.log("Test for "+ input + ": ", isDescending(input)))
We can use this kind of approach which will tell us whether we are the number in ascending or descending order
AscendingOrDescending = (inputString) => {
const arrayOfNumbers= Array.from(inputString);
const asscendingResult = arrayOfNumbers.every((number,index,array) => {
return index < array.length ? Number(number) + 1 === Number(array[index+1]) || Number(number) - 1 === Number(array[index-1]) : true;
});
return asscendingResult ? 'string is in ascending or descending order' : 'string is not in ascending or descending order'
}
console.log(AscendingOrDescending("123456"))
I need to increment a value similar to this:
A001 becomes A002
A999 becomes B001
B001 becomes B002
etc
Z999 becomes A001
I can increment an integer like this:
var x = 5;
x++;
Yields x = 6
I can increment an character like this:
var str = 'A';
str = ((parseInt(str, 36)+1).toString(36)).replace(/0/g,'A').toUpperCase();
if (str =='1A') {
str = 'A';
}
Yields the next character in the alphabet.
This code seems to work, but I'm not sure it's the best way?
var str = 'Z999';
if (str == 'Z999') {
results = 'A001';
}
else {
var alpha = str.substring(0,1);
num = str.substring(1,4);
if (alpha != 'Z' && num == '999') {
alpha= ((parseInt(alpha, 36)+1).toString(36)).replace(/0/g,'A').toUpperCase();
}
num++;
var numstr = num + "";
while (numstr .length < 3) numstr = "0" + numstr ;
if (numstr == 1000) {
numstr = '001';
}
results = alpha + numstr;
}
results seems to give the correct answer. Yes?
You could use parseInt(input.match(/\d+$/), 10) to extract the number at the end of the string and input.match(/^[A-Za-z]/) to retreive the single character at the beginning.
Increment and pad the number accordingly, and increment the character if the number is over 999 by retrieving the character's character code and incrementing that.
String.fromCharCode(letter.charCodeAt(0) + 1);
Full code/example:
function incrementNumberInString(input) {
var number = parseInt(input.trim().match(/\d+$/), 10),
letter = input.trim().match(/^[A-Za-z]/)[0];
if (number >= 999) {
number = 1;
letter = String.fromCharCode(letter.charCodeAt(0) + 1);
letter = letter === '[' ? 'A': (letter === '{' ? 'a' : letter);
} else {
number++;
}
number = '000'.substring(0, '000'.length - number.toString().length) + number;
return letter + number.toString();
}
document.querySelector('pre').textContent =
'A001: ' + incrementNumberInString('A001')
+ '\nA999: ' + incrementNumberInString('A999')
+ '\nB001: ' + incrementNumberInString('B001')
+ '\nB044: ' + incrementNumberInString('B044')
+ '\nZ999: ' + incrementNumberInString('Z999');
<pre></pre>
Output:
A001: A002
A999: B001
B001: B002
B044: B045
D7777: E001
Try storing A-Z in an array , using String.prototype.replace() with RegExp /([A-Z])(\d+)/g to match uppercase characters , digit characters . Not certain what expected result is if "Z999" reached ?
var arr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
var spans = document.querySelectorAll("span");
function count(el) {
var data = el.innerHTML.replace(/([A-Z])(\d+)/g, function(match, text, n) {
var _text, _n;
if (Number(n) === 999) {
_text = arr[ arr.indexOf(text) + 1 ];
} else {
_text = text
};
// `"Z999"` condition ?
if (_text === undefined) {
return "<mark>" + text + n + "</mark>"
}
_n = Number(n) + 1 < 1000 ? Number(n) + 1 : "001";
if (n < 10) {
return _text + n.slice(0, 2) + _n
};
if (n < 100) {
return _text + n.slice(0, 1) + _n
} else {
return _text + _n
}
});
el.innerHTML = data
}
for (var i = 0; i < spans.length; i++) {
count(spans[i])
}
<span>A001</span>
<span>A999</span>
<span>B001</span>
<span>C999</span>
<span>D123</span>
<span>Z999</span>