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));