I was wondering if there's a possibility in velocity to use regex capturing groups? So, as a simple example, we may want to get "Some Name", "13:00" and "14:00" from the following input string: "Some Name: 13:00 - 14:00".
There are multiple ways how this can be achieved but one possibility would be to use a simple regex such as "^(.+):\s?(\d{1,2}:\d{1,2})\s?\-\s?(\d{1,2}:\d{1,2})$" and then extract the parts from the three capturing groups.
Something like "matches" in Velocity only returns a Boolean which can be used to e.g. compare a input to a pattern.
#set( $input = "Some Name: 13:00 - 14:00" )
#set( $regex = "^(.+):\s?(\d{1,2}:\d{1,2})\s?\-\s?(\d{1,2}:\d{1,2})$" )
#if ( $input.matches($regex) )
## We have a match
#end
In JavaScript land you could e.g. do something simple such as
const [_, name, timeFrom, timeTo] = "Some Name: 13:00 - 14:00".match(/^(.+):\s?(\d{1,2}:\d{1,2})\s?\-\s?(\d{1,2}:\d{1,2})$/i) || [];
Is there something similar in Velocity? Given the example above, we could "split" by multiple delimiters or use multiple "replace" runs to get the parts but that obviously has its limitations and feels somewhat hacky.
Maybe @SanfordWhiteman has an idea? Thanks in advance.
Solved! Go to Solution.
It's not so much capturing groups you're lacking (which are supported) it's an iterable set of match results.
In Marketo's Velocity, you can't create a Matcher object (other Velocity installs do support this).
The closest way to simulate would be replaceAll and put a known-unused delimiter - like the ASCII record delimiter in the below example - then split on the delimiter.
#set( $input = "Some Name: 13:00 - 14:00" )
#set( $regex = "^(.+):\s?(\d{1,2}:\d{1,2})\s?\-\s?(\d{1,2}:\d{1,2})$" )
#set( $output = $input.replaceAll( $regex, "$1\u001e$2\u001e$3" ).split("\u001e") )
#foreach( $part in $output )
${part}
#end
Figure I'll do a blog post on this one of these days.
It's not so much capturing groups you're lacking (which are supported) it's an iterable set of match results.
In Marketo's Velocity, you can't create a Matcher object (other Velocity installs do support this).
The closest way to simulate would be replaceAll and put a known-unused delimiter - like the ASCII record delimiter in the below example - then split on the delimiter.
#set( $input = "Some Name: 13:00 - 14:00" )
#set( $regex = "^(.+):\s?(\d{1,2}:\d{1,2})\s?\-\s?(\d{1,2}:\d{1,2})$" )
#set( $output = $input.replaceAll( $regex, "$1\u001e$2\u001e$3" ).split("\u001e") )
#foreach( $part in $output )
${part}
#end
Figure I'll do a blog post on this one of these days.
Great, thanks, that's what I was looking for.
Hi Sanford,
Just found this solution, and it's great! thank you! However, I did have a follow up question.
Is it possible assign the regex groups to unique variables?
I tried to set up a script using something similar to this line of code you provided:
#set( $output = $input.replaceAll( $regex, "$1\u001e$2\u001e$3" ).split("\u001e") )
Here's what I was trying to do for that portion of the script:
#set( $outputA = $input.replaceAll( $regex, "$1" ) )
#set( $outputB = $input.replaceAll( $regex, "$2" ) )
${outputA}
<br>
${outputB}
In my case, I'm trying to extract 2 URLs from a field. I needed them in variables so that I can link specific text within the email.
Using the code you created, I'm able to display the URLs correctly, but just not sure how to modify it for my needs.
Thank you!
There’s no destructing assignment, if that’s what you mean.
You can do runtime dynamic variable creation like this, but (a) I don‘t advise it because it looks clumsy and is hard to follow and (b) you can’t use it with URLs in any case:
#set( $partNames = ["name", "start", "end"] )
#foreach( $part in $partNames )
#evaluate( "${esc.h}set( ${esc.d}$part = ${esc.d}output[$foreach.index] )" )
#end
If you already have the $output (the part that Sanford mentioned), I'd say that you can also do something along these lines:
#set( $output = $input.replaceAll( $regex, "$1\u001e$2\u001e$3" ).split("\u001e") )
#set( $outputA = $output[0] )
#set( $outputB = $output[1] )
#set( $outputC = $output[2] )