ndoes
ndoes

Reputation: 678

Javascript regex: Ignore closing bracket when enclosed in parentheses

I have a regex with a couple of (optional) capture groups. I'm trying to add a new feature that allows a user to add content to one of the capture groups that matches the closing bracket of the regex. I'm struggling to ignore this match

The current regex is /\[(.+?)=(.+?)(?:\|(.+?))?(?:\:(.+?))?\]/g

This allows a user to target data according to:

[key=value|filter:filtervalue]

where filter and filtervalue are optional.

The problem is that for the value it should now be possible to target indexes in an array. For example:

[data=example(products[0].id)]

However, the regex matches only up to .id so the second capture group is example(products[0. I would like it to be example(products[0].id). I think I should be fine if I can ignore the closing bracket when it is wrapped by parentheses, but I've been unable to figure out how.

Examples that should be matched:

[data=example(products[0].id)]
[data=example(products[index].id)]
[data=regular]
[data=test|number:2]

I created a regex101. Any help is appreciated.

Upvotes: 1

Views: 1592

Answers (2)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626689

You may use

/\[([^=\]]+)=((?:\[[^\][]*]|[^\]])+?)(?:\|([^:\]]+):((?:\[[^\][]*]|[^\]])+?))?]/g

See the regex demo

Note that lazy dot matching patterns are replaced with more restrictive negated character classes to make sure there is no overflow from one part of the bracketed string to another. To allow matching 1st-level nested [...] substrings, a \[[^\][]*]|[^\]] pattern is used to match [...] or non-] substrings is used.

Details:

  • \[ - a literal [
  • ([^=\]]+) - Group 1 capturing 1+ chars other than = and ]
  • = - a = symbols
  • ((?:\[[^\][]*]|[^\]])+?) - Group 2 capturing 1+ occurrences (as few as possible) of:
    • \[[^\][]*] - [, then 0+ chars other than ] and [, and then ]
    • | - or
    • [^\]] - any char other than ]
  • (?:\|([^:\]]+):((?:\[[^\][]*]|[^\]])+?))? - an optional group matching:
    • \| - a | char
    • ([^:\]]+) - Group 3 capturing 1+ chars other than : and ]
    • : - a colon
    • ((?:\[[^\][]*]|[^\]])+?) - Group 4 capturing the same text as Group 2.
  • ] - a literal ] char.

Upvotes: 1

Twelleby
Twelleby

Reputation: 304

I would probably break it down to two separate regular expressions and "or" them. Below I have used your expression to match the first kind of string:

\[(?:(.+?)=(.+)(?:\|(.+?)?\:(.+?)))\]
[key=value|filter:filtervalue]

And another for the second one:

\[(.+?)=(.+)\]
[data=example(products[0].id)]

Then concatenating them to:

\[(?:(.+?)=(.+)(?:\|(.+?)?\:(.+?))|(.+?)=(.+))\]

Where it first tries to match the tricky part and if that fails, resorts to the more general one.

https://regex101.com/r/d6LwEt/1

Upvotes: 0

Related Questions