Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove unnecessary parenthesis?

Same as this but JavaScript. A few examples to illustrate my goal:

  • (((foo))) => (foo)
  • ((foo)) => (foo)
  • (foo) => (foo)
  • (foo (bar)) => (foo (bar))
  • ((foo b)ar) => ((foo b)ar)
  • (((a)b(c))) => ((a)b(c))

I have made a regex which should match the ones I want to change /\({2,}[\s\S]*\){2,}/g but I can't seem to figure out how to remove them.

Is there something like String.replace(/\({2,}[\s\S]*\){2,}/g, '(${rest})')?

like image 404
leonheess Avatar asked Aug 08 '19 10:08

leonheess


2 Answers

Instead of struggling with regexes I'd suggest you try the classic tokenize-parse-generate workflow. The idea is to parse a string into a data structure (in this case, nested arrays), simplify that structure and generate a new string from it.

Example:

function tokenize(str) {
    return str.match(/[()]|[^()]+/g);
}

function parse(toks, depth = 0) {
    let ast = [];

    while (toks.length) {
        let t = toks.shift();
        
        switch (t) {
            case '(':
                ast.push(parse(toks, depth + 1));
                break;
            case ')':
                if (!depth)
                    throw new SyntaxError('mismatched )');
                return ast;
            default:
                ast.push(t);
        }
    }

    if (depth) {
        throw new SyntaxError('premature EOF');
    }

    return ast;
}

function generate(el) {
    if (!Array.isArray(el))
        return el;

    while (el.length === 1 && Array.isArray(el[0]))
        el = el[0];

    return '(' + el.map(generate).join('') + ')';
}


//

let test = `
(((foo)))
((foo))
(foo)
(foo (bar))
((foo b)ar)
((((foo)) bar))
((((((foo))bar))baz))
((((((((foo))))))))
foo
`;

for (let s of test.trim().split('\n'))
    console.log(s, '=>', generate(parse(tokenize(s))));
like image 167
georg Avatar answered Oct 07 '22 13:10

georg


You can try this:

'(((foo))) => (foo)'.replace(/(\({2,})([a-zA-Z]*)(\){2,})/g, '($2)')

Or in general form:

str.replace(/(\({2,})([a-zA-Z]*)(\){2,})/g, '($2)')

I changed your regex a bit to capture the text matched by the regex in as numbered groups, so it is possible to use backreferences in string.replace().

So basically you have 3 groups and you can reference them:

  • $1 is gonna be open parenthesis like ((, (((, etc.
  • $2 is the content between ((( content )))
  • $3 is gonna be close parenthesis like )), ))), etc.

Happy Hacking :)

like image 28
ikos23 Avatar answered Oct 07 '22 13:10

ikos23