I have a template string which has merge fields:
Example:
let templateStr = '[!field]: value entered is too large. (max length=[!limit])';
I have an error message string which follows the above mentioned template format, here the field name and max length can vary according to the field that user is trying to save.
Example:
let errorMessage = 'Country: value entered is too large. (max length=80)';
Here I am trying to extract the variables (field, limit) referenced in the templateStr based on the errorMessage.
extractTemplateVar(errorMessage, templateStr) // should return field='Country' and limit='80'
if errorMessage = 'Email: value entered is too large. (max length=100);
then,
extractTemplateVar(errorMessage, templateStr) // should return field='Email' and limit='100'
I have tried using regex, to match and extract variables from string based on template. its not working as intended
let pattern = '(.+): data value too large: .*(max length=(.+))';
new RegExp(pattern ).exec(errorMessage);
Guys, please help me resolve this issue. Thanks in advance guys.
This is a novel task indeed. Here's the steps I'd take to tackle this:
[!TEMPLATE_NAME]) using regexAnd here's your code:
let templateStr = '[!field]: value entered is too large. (max length=[!limit])';
let errorMessage = 'Country: value entered is too large. (max length=80)';
function extractTemplateVar(str, template) {
const match = /\[!.+?\]/g,
data = [],
result = {},
escapeRegex = str => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
let matching;
while(matching = match.exec(template)) {
// go through everything that looks like a template and get the text before and after it
const index = matching.index;
const str = matching[0];
const text = str.match(/(?<=\[!).+?(?=\])/)[0]; // extract the exact text from the template
const endIndex = str.length + index;
const front = escapeRegex(template.substr(0, index).replace(/\[!.+?\]/g, "::TMP::")); // text before
const back = escapeRegex(template.substr(endIndex).replace(/\[!.+?\]/g, "::TMP::")); // text after
data.push({ text, back, front });
}
data.forEach(n => {
const re = new RegExp(`(?<=${n.front.replaceAll("::TMP::", ".*?")}).+?(?=${n.back.replaceAll("::TMP::", ".*?")})`); // create regex with dynamic lookaround
const findText = str.match(re)[0];
result[n.text] = findText;
});
return result;
}
console.log(extractTemplateVar(errorMessage, templateStr));
Based on your example strings, as a pattern you could use 2 capture groups:
([^:\n]+):.*?\(max length=([^\s=()]+)\)
The pattern matches:
([^:]+) Capture group 1, match 1+ times any character except for a : or newline:.*? Match : and the rest of the line no greedy\(max length= Match (max length=
([^\s=()]+) Capture group 2, match 1+ times any character except for = ( )\) Match )const regex = /([^:\n]+):.*?\(max length=([^\s=()]+)\)/;
const errorMessage = 'Email: value entered is too large. (max length=100)';
const m = errorMessage.match(regex);
if (m) {
console.log(`field='${m[1]}' and limit='${m[2]}'`);
}
If you want to match only digits for the limit, you can also use \d+
([^:\n]+):.*?\(max length=(\d+)\)
This is a bit gross. There might be a better way.
Part 1, extract the variable names from the template.
Part 2, split the template by those names to get the "rest of the template"
Part 3, convert "rest of template" fragments to regex compatible format. I probably missed a few things that need to be escaped
Part 4, join the template fragments back to a regex-compatible string, separated by (.*?) - which will capture the actual values of the variables
Part 5, run the constructed regex against the message, to extract variable values
Part 6, generate a return structure from the variable names and values
let templateStr = '[!field]: value entered is too large. (max length=[!limit])';
let errorMessage = 'Country: value entered is too large. (max length=80)';
const extractTemplateVars = (template, message) => {
let regex = /\[\!(.*?)\]/g;
let splitRegex = /\[\!.*?\]/g;
let match, matches = [];
// Part 1
while (match = regex.exec(template)) {
matches.push(match[1]);
}
// Part 2
let arr = template.split(splitRegex);
// Part 3
arr = arr.map(s =>
s.replaceAll("\\", "\\\\")
.replaceAll("(", "\\(")
.replaceAll(")", "\\)")
.replaceAll(".", "\\.")
);
// Part 4
let reStr = arr.join("(.*?)");
let re = new RegExp(reStr, "g");
// Part 5
let m = re.exec(message);
let [garbage, ...values] = m;
// Part 6
let output = matches.map((match, i) => ({[match]: values[i]}));
console.log(output);
};
extractTemplateVars(templateStr, errorMessage);