ETHproductions
ETHproductions

Reputation: 499

Replacing only the first match of a global regex

I'm writing a function to recursively replace matches of a regex in a string. The replacement can be a function, as with vanilla .replace, and this function can access the original string through one of its arguments.

I would like my function to replace only one match on each iteration. With a non-global regex, this will always be the case. However, some of the regexes this function receives will be global. Doing a traditional .replace(regex, replacement) means it could replace multiple times on each iteration, not only messing up the order in which the matches are processed, but also passing an incorrect index and original string to the replacement function.

As an example:

function recursiveReplace(string, regex, replacement) {
  for (var i = 1e8; i > 0 && regex.test(string); i--)
    string = string.replace(regex, replacement);
  return string;
}

console.log(
  recursiveReplace("abcdef", /../g, function (match, index, original) {
    console.log(original);
    return match[0];
  })
);

This outputs

abcdef
abcdef
abcdef
ace
ae
a

when the desired output is

abcdef
acdef
adef
aef
af
a

How can I get the function to process only one match on each iteration, whether the regex has the g flag or not? Note that I'm using the function in such a way that the second argument will always be a regex (I have no control over this, nor over whether or not said regex has the g flag).

Upvotes: 7

Views: 1169

Answers (1)

ETHproductions
ETHproductions

Reputation: 499

It would seem that the best way to do this would be to just manually remove the g flag from the regex. Here's the most cross-platform way I could find to do this, using regex.toString() to get the string representation of the regex:

function recursiveReplace(string, regex, replacement) {
  regex = eval(regex.toString().replace(/[a-z]*$/, function (s) {
    return s.replace('g', '');
  }));
  for (var i = 1e8; i > 0 && regex.test(string); i--)
    string = string.replace(regex, replacement);
  return string;
}

With the ES6 features RegExp(regex) and RegExp#flags this gets much easier:

function recursiveReplace(string, regex, replacement) {
  regex = RegExp(regex, regex.flags.replace('g', ''));
  for (var i = 1e8; i > 0 && regex.test(string); i--)
    string = string.replace(regex, replacement);
  return string;
}

Upvotes: 2

Related Questions