简体   繁体   中英

JavaScript - RegExp - Replace useless parentheses in string

So I have a string like this, ready to be evaluated:

"getInfo(((((2+2)*(3/4) / ((44))))))"

So, there's 1 place where a tripple parenthese is that is useless and 1 place where a double one is useless, so the string can be simplyfied like this:

"getInfo((2+2)*(3/4) / (44))"

In order to sort these unnesseccary parentheses out you can just replace every 2 useless parentheses with 1 parenthese, like this:

"do((((2+2 - 3)*(2))))" -> "do(((2+2 - 3)*(2)))" -> "do((2+2 - 3)*(2))"

Is there anyway either RegExp or looping using the String.replace method to do this, and how?

This seems to work:

function simplify(str) {
    var i=0;
    return (function recur(s, b) {
        var c = str.charAt(i++);      // Get next char    
        if(!c || c == ')') return s;  // End of string or end of inner part
        if(c == '(') {
            var s1 = recur('', true), // Get inner part
                s2 = recur('');       // Get following part
            return s + (!b || s2 ? '('+s1+')' : s1) + s2;
        }
        return recur(s+c);            // Continue to next char
    })('');
}

This code should be equivalent, with less function calls:

function simplify(str) {
    var i=0;
    return (function recur(b) {
        var c, s = '';
        while(c = str.charAt(i++)) {          // Keep getting chars
            if(c == ')') return s;            // End of inner part
            if(c == '(') {
                var s1 = recur(true),         // Get inner part
                    s2 = recur();             // Get following part
                return s + (!b || s2 ? '('+s1+')' : s1) + s2;
            }
            s += c;                           // Add current char
            b = false;
        }
        return s;
    })();
}

The part which gets rid of unnecessary parentheses is this one:

return s + (!b || s2 ? '('+s1+')' : s1) + s2;

Basically, it returns the current string, concatenated with the inner part (maybe parenthesized), concatenated with the following part.

The inner part is parenthesized either if the following part is not empty, or if b is falsy (that is, if the inner part is not already immediately wrapped in parentheses).


Note the codes above expect a well-formed expression. If not,

  • The returned string will be truncated if there is a ) without a corresponding (

     simplify("a)b"); // "a" 
  • The returned string will have additional ) at the end if there are unclosed ( :

     simplify("a(b"); // "a(b)" 

I'm not incredibly skilled in javascript, but this seemed like an interesting question, so I'll try to work the logic on it for you. You can do this programmatically by working from the 'inside out,' identifying valid paren pairs and 'removing' them from the string. Given your original example:

  • Traverse from left to right until the first occurrence of closing paren ")". Here, it would be 15 in a zero-based character array.

     getInfo(((((2+2)*(3/4) / ((44)))))) 0123456789012345 
  • Traverse backwards until opening paren "(". Here it would be 11.

     getInfo(((((2+2)*(3/4) / ((44)))))) 012345678901 
  • If the string segment between these two positions contains at least one non-whitespace character, this is a valid parenthesis pair, so it is to be preserved. You "remove" this group from evaluation then loop, finding the next closing paren (in this case, at position 21).

     getInfo((((#####*(3/4) / ((44)))))) 0123456789012345678901 
  • Working backwards to the next previous open paren at 17, and "removing."

     getInfo((((#####*##### / ((44)))))) 012345678901234567 
  • Again (26 to 29):

     getInfo((((#####*##### / (####))))) 012345678901234567890123456789 
  • Again, but this time there are no non-whitespace characters in between the positions (30 and 25), so we record those two positions for removal.

     getInfo((((#####*##### / X####X)))) 0123456789012345678901234567890 
  • Repeat (31 to 20), a valid section because of the division and multiplication symbols:

     getInfo(((############ / X####X#))) 012345678901234567890123456789012 
  • Again (32 and 9), but this time another non-valid group, so mark those positions:

     getInfo((X###############X####X#X)) 012345678901234567890123456789012 
  • Again (33 and 8):

     getInfo(XX###############X####X#XX) 0123456789012345678901234567890123 
  • Finally (34 to 7):

     getInfoXXX###############X####X#XXX 01234567890123456789012345678901234 
  • There are no more remaining close parens, so now we take our array of character positions marked for removal, and remove those characters. In the example string, that's 30, 25, 32, 9, 33, 8, 34, and 7. You'll probably have to start with the highest character position and work your way backwards, otherwise you'd be affecting the positions of the rest of the characters in the string. So, something like (pseudo-code, naturally):

     removalArray.sort(ascending) for (int x=removalArray.length; x>0; x--) { myString.removeCharacter(removalArray(x)) } 

Doing that gives us:

    getInfo((2+2)*(3/4) / (44))

Sorry for the long response. Best of luck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM