0

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;
        }

}
JimmyB
  • 99
  • 1
  • 8
  • 4
    Tricky problem indeed, but luckily solved some 70+ years ago: https://en.wikipedia.org/wiki/Shunting_yard_algorithm – gog Oct 07 '22 at 18:22
  • thank you! I didnt know about it! Well, i guess i just have to look for some javascript solution with the name of the algorithm, and then, i will put the solution here once it worked. – JimmyB Oct 07 '22 at 18:25
  • Well, I prefer [LL parsers](https://en.wikipedia.org/wiki/LL_parser)... – kelsny Oct 07 '22 at 18:25
  • Is `eval()` out of the picture here? `console.log("Result: %d", eval("2 + 3 * 4 / 2 - 1")`. – José Ramírez Oct 07 '22 at 18:48
  • yep, dont want to use eval, to me it's a bad idea – JimmyB Oct 07 '22 at 19:01
  • 1
    The question has been asked before, and although the duplicate reference at the top has many answers saying "you *should* use eval", you'll also find answers implementing Shunting Yard like algorithms, including [one I posted](https://stackoverflow.com/a/47761792/5459839) – trincot Oct 07 '22 at 21:05

1 Answers1

0

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);
}
JimmyB
  • 99
  • 1
  • 8
  • What's the problem with using regular expressions? – rici Oct 08 '22 at 00:37
  • For better performances. RegEx makes lower performances. So far, it works well, i think i will declare some more variables and make some loop to filter without a regEx. – JimmyB Oct 08 '22 at 08:53