skip to Main Content

This is a direct follow up of my previous question where I got the following Regex;

const matches = text.match(/(?:([^()]*(?:([^()]*)[^()]*)*)|[^,])+/g);

From this a,(b, b),c (aaa, (bbb, cccc, ddd)),d I get

a
(b, b)
c (aaa, (bbb, cccc, ddd))
d

But it fails when I have the following case a,(b, b),c (aaa, ((b b), cccc, ddd)),d where there are 3 nested parentheses which is logical after dissecting how the Regex works.

I tried to update it to consider another level of parentheses and I did the following

const matches = text.match(/(?:([^()]*(?:([^()]*(?:([^()]*)[^()]*)*)[^()]*)*)|[^,])+/g)

It works (online demo) but I am not sure if it’s the optimal solution. I also don’t know if it will cover all the cases. Can anyone confirm? or maybe there is a better Regex.

I am also looking for a way to generate such Regex for a giving number of parentheses. I have it for 2 and 3 but what about N? Will it work if I always repeat the following part (?:([^()]*)[^()]*)* recursively? I know Regex cannot handle any number of nested parentheses but I am not looking for this. I want for a giving number to generate the regex (using JS) and use it.

2

Answers


  1. See this answer below JS section > Without Recursion. I just added a JS snippet there to generate pattern for a chosen max depth. Modified the snippet to your needs (non-commas before/after).

    // JS-Snippet to generate pattern
    function generatePattern()
    {
      // Set max depth & pattern parts -> build pattern
      let depth = document.getElementById("maxDepth").value;
      let p = ['\([^)(]*(?:','\([^)(]*\)','[^)(]*)*\)'];
      console.log('(?:' + p[0].repeat(depth) + p[1] + p[2].repeat(depth) + '|[^,])+');
    } generatePattern();
    Max depth = <input type="text" id="maxDepth" size="1" value="2"> 
    <input type="submit" onclick="generatePattern()" value="generate pattern!">

    The example you provided looks to me like an optimal solution (covers 2 levels nesting). It already contains an unrolled version and is efficient. I doubt there are many options for more optimization.

    Login or Signup to reply.
  2. Most of the time a single regex isn’t the right tool when you need to track nesting depth. For these cases you’ll probably want to use the programming language to parse the string.

    For this scenario a simple parser could look like this:

    function parseArgString(string) {
      const args = [];
      let argStartIndex = 0;
      let depth = 0;
      
      for (let index = 0; index < string.length; ++index) {
        const char = string[index];
        
        if (char == "(") depth += 1;
        if (char == ")") depth -= 1;
        if (depth < 0) throw new Error('unexpected ")" character');
        
        if (char == "," && !depth) {
          args.push(string.slice(argStartIndex, index).trim());
          argStartIndex = index + 1;
        }
      }
      
      const finalArg = string.slice(argStartIndex).trim();
      if (finalArg.length) args.push(finalArg);
      
      return args;
    }
    
    const argString = "a,(b, b),c (aaa, ((b b), cccc, ddd)),d";
    console.log(parseArgString(argString));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search