Suppose we want to create a replacer that appends the offset data to every matched string. Because the replacer function already receives the offset parameter, it will be trivial if the regex is statically known.
"abcd".replace(/(bc)/, (match, p1, offset) => `${match} (${offset}) `);
However, this replacer would be hard to generalize if we want it to work with any regex pattern. The replacer is variadic — the number of arguments it receives depends on the number of capturing groups present. We can use rest parameters, but it would also collect offset, string, etc. into the array. The fact that groups may or may not be passed depending on the identity of the regex would also make it hard to generically know which argument corresponds to the offset.
function addOffset(match, ...args) {
const offset = args.at(-2);
return `${match} (${offset}) `;
}
console.log("abcd".replace(/(bc)/, addOffset));
console.log("abcd".replace(/(?<group>bc)/, addOffset));
The addOffset example above doesn't work when the regex contains a named group, because in this case args.at(-2) would be the string instead of the offset.
Instead, you need to extract the last few arguments based on type, because groups is an object while string is a string.
function addOffset(match, ...args) {
const hasNamedGroups = typeof args.at(-1) === "object";
const offset = hasNamedGroups ? args.at(-3) : args.at(-2);
return `${match} (${offset}) `;
}
console.log("abcd".replace(/(bc)/, addOffset));
console.log("abcd".replace(/(?<group>bc)/, addOffset));