1

I have two questions actually. What I want to do is 1st to check if the user entered value is a correct mathematical equation. For example, if the use enters x + y ( z this should detect as an invalid formula and x + y ( z ) as a correct one,

The 2nd thing I want to do is to split the formula from the + - * / () signs, so the above formula will be return as x, y, z.

What I have done is bellow

var arr = [];

var s = 'x + y ( z )';

arr = s.split("(?<=[-+*/])|(?=[-+*/])");

console.log(arr);

This returns a single array with just one data like, [x + y ( z )]

Another thing, the variables are not single letters. they could be words like, annual price, closing price, etc

Can someone help me in this problem. Thanks in advance

UPDATE : I have tried "/[^+/*()-]+/g" also

Ravindu
  • 2,408
  • 8
  • 30
  • 46

3 Answers3

4

For the second part:

var s = 'x + y ( z )';
var arr = s.match(/(\w)/g);
console.log(arr);
document.write(JSON.stringify(arr));

Of course, you have to check the validity of the input first.

Edit: using Tomer W's answer suggesting eval():

function checkExpression(str) {
    // check for allowed characters only
    if (/[^\w\d\(\)\+\*\/\-\s]/.exec(s) != null)
        return false;

    // extract variable names, assuming they're all one letter only
    var arr = s.match(/(\w+)/g);

    // instantiate the variables
    arr.forEach(function (variable) {
        eval(variable + '= 1');
    });

    // ( is alone, replace it by * (
    str = str.replace(/\(/g, '* (');

    try {
        eval(str);
        return true;
    }
    catch (ex) {
        console.log(ex);
        return false;
    }
}

It's dirty but it works most of the time (Tomer W pointed some edge cases like ++62+5 or 54++++6 that can be avoided with an another regex check), do you have more complicated example to test?

Shanoor
  • 13,344
  • 2
  • 29
  • 40
  • Above expression will split the arr from % sign also, what I want is to split only from / * - + ( ) – Ravindu Nov 23 '15 at 06:42
  • Yes, you have to check for validity first. A "lazy" solution here is generating a parser, the example given here is exactly what you want but the generated code is 18Ko: http://pegjs.org/online – Shanoor Nov 23 '15 at 06:46
  • I cant use a parser because there are no valid values for variables in the formula. its just variables only. I just want to make sure its a valid formula – Ravindu Nov 23 '15 at 07:07
  • actually yes. Since the formula is a user input one, it can be anything. Thats the reason I want to chech the validaty of the formula. User can enter formulas like, x*2+y(4 . whic is incorrect. missing ). I should have validate this kind of formulas and give an error – Ravindu Nov 23 '15 at 08:23
  • another thing, the variables are not single letters. they could be words like, annual price, closing price, etc – Ravindu Nov 23 '15 at 08:24
  • I have created a validator for this purpose. Pls check my answer given and give your feedback and any suggestions for that – Ravindu Nov 24 '15 at 07:16
1

Word of warning

VERY VERY VERY DANGEROUS METHOD AHEAD !!!

I am posting this as it is a valid answer, but you should do it only with extreme caution as a user can totally mess up your site.
DO not let the one user input be used in eval for another user !!! EVER !!!

Actual answer

You can use the built-in java-script compiler of your browser, and use eval()

function checkEq(equ)
{
  for(ch in equ){
    // check that all characters in input are "equasion usable"
    if(" +-*/1234567890e^&%!=".indexOf(ch) === -1)
    { // if there are invalid chars
      return false;
    }
  }
  try{
    // try running the equ, will throw an exception on a syntax error.
    eval(equ);
    return true; // no exception
  }
  catch(ex){
    return false; // syntax error
  }
}

Plunker example

and as i noted before! extreme caution!

Tomer W
  • 3,395
  • 2
  • 29
  • 44
  • I tried eliminating usage of most chars in `eval` but a nifty hacker might be able to craft something artistic using `##;` (though he has no `;`) – Tomer W Nov 23 '15 at 07:20
  • Except the input is filled of variables names so it will always fail (ReferenceError everywhere) but using my answer to get the variables name then creating them before using your eval could work. It's dangerously clever :D – Shanoor Nov 23 '15 at 07:22
  • you can allow variables as `x` `y` `z` too, but this will open the user to write smth like `my_nasty_function();` :) – Tomer W Nov 23 '15 at 07:22
  • Found another issue to take care :) as `54+++6` is valid and results in `60`, also `++62+5` result in `68` :) – Tomer W Nov 23 '15 at 07:25
  • I updated my answer with your input, using `eval()` is dirty but it works quite good for so few lines (as long as the input is safe). I would still use a real parser though, to cover all cases. – Shanoor Nov 23 '15 at 07:41
  • I have created a validator for this purpose. Pls check my answer given and give your feedback and any suggestions for that – Ravindu Nov 24 '15 at 07:16
0

Using both @ShanShan and @Tomer W answers, I wrote a formula validator. the code is bellow. In this validator, I checks for opening and closing brackets, extra / * - + signs, unwanted symbols, etc.

If the user wants to create and validate a formula with x,y,z variables, user have to add the variables to an array and pass it to this validator with the formula written so the validator accepts the variables which are not numbers.

I also used a custom set mentioned in this post. https://stackoverflow.com/a/4344227/918277

Important : This java script function can't validate very complex formulas with sin, cos, tan, log, etc. Script will identify them as just letters and will return false.

function StringSet() {
    var setObj = {}, val = {};

    this.add = function(str) {
        setObj[str] = val;
    };

    this.contains = function(str) {
        return setObj[str] === val;
    };

    this.remove = function(str) {
        delete setObj[str];
    };

    this.values = function() {
        var values = [];
        for ( var i in setObj) {
            if (setObj[i] === val) {
                values.push(i);
            }
        }
        return values;
    };
}

/**
 * 
 * @param _formulaInputs
 *            Array of use entered inputs to be use in formula
 * @param _formula
 *            User entered formula
 * @returns {Boolean}
 */
function validateFormula(_formulaInputs, _formula) {

    var formula = _formula;

    var bracketStack = new Array();

    var formulaDescArray = [];

    var formulaDescInForm = new StringSet();

    for (var i = 0; i < _formulaInputs.length; i++) {

        formulaDescInForm.add(_formulaInputs[i]);
    }

    /* Regex to check for unwanted symbols(! @ # $ etc.) */
    if (/[^\w\d\(\)\+\*\/\-\s]/.exec(formula) != null) {
        return false;
    }

   for (var i = 0; i < _formula.length; i++) {
            if ((_formula.charAt(i) == '/' || _formula.charAt(i) == '*'
                    || _formula.charAt(i) == '-' || _formula.charAt(i) ==   '+')
                    && (_formula.charAt(i + 1) == '/'
                            || _formula.charAt(i + 1) == '*'
                            || _formula.charAt(i + 1) == '-' || _formula
                            .charAt(i + 1) == '+')) {
                return false;
            }
    }

    var lastChar = formula.charAt(formula.length - 1);

    if (lastChar == '/' || lastChar == '*' || lastChar == '-'
            || lastChar == '+') {
        return false;
    }

    formulaDescArray = formula.split(/[\/\*\-\+\()]/g);

    /* Remove unwanted "" */
    for (var i = 0; i < formulaDescArray.length; i++) {
        if (formulaDescArray[i].trim().length == 0) {
            formulaDescArray.splice(i, 1);
            i--;
        }
    }

    /* Remove unwanted numbers */
    for (var i = 0; i < formulaDescArray.length; i++) {

        if (!isNaN(formulaDescArray[i])) {
            formulaDescArray.splice(i, 1);
            i--;
        }
    }

    for (var i = 0; i < formulaDescArray.length; i++) {
        if (!formulaDescInForm.contains(formulaDescArray[i].trim())) {
            return false;
        }
    }

    for (var i = 0; i < formula.length; i++) {
        if (formula.charAt(i) == '(') {
            bracketStack.push(formula.charAt(i));
        } else if (formula.charAt(i) == ')') {
            bracketStack.pop();
        }
    }

    if (bracketStack.length != 0) {
        return false;
    }

    return true;

}
Community
  • 1
  • 1
Ravindu
  • 2,408
  • 8
  • 30
  • 46