802

What is the fastest way to replace all instances of a string/character in a string in JavaScript? A while, a for-loop, a regular expression?

thorn0
  • 9,362
  • 3
  • 68
  • 96
Anriëtte Myburgh
  • 13,347
  • 11
  • 51
  • 72
  • 4
    A while and a for-loop both would run in O(n) with a simple algorithm. Not really sure what's the time complexity for Javascript regex-engine in this case, but my guess is its optimized enough to run in O(n) for a simple string match. – Anurag Jan 22 '10 at 10:33
  • 3
    This seems like micro-optimising to me - did performance profiling show up the string replacement to be the slowest part of your program? – JBRWilkinson Jan 22 '10 at 11:03
  • No, I didn't put my script through performance profiling, I was just making sure I'm using the fastest function available. – Anriëtte Myburgh Aug 31 '10 at 23:16
  • 5
    I've done a JSPerf comparing global regex and a for-loop: http://jsperf.com/javascript-replace-all. If I've written the tests appropriately, it looks like the answer is "it depends". – Paul D. Waite Nov 13 '12 at 12:22
  • According to this http://jsperf.com/split-join-vs-replace/16 the fastest method is to use `split join` – TheGr8_Nik Dec 01 '14 at 14:10
  • 1
    Possible duplicate of [Replacing all occurrences of a string in JavaScript](http://stackoverflow.com/q/1144783/1529630) – Oriol Mar 26 '16 at 00:18
  • I'm not sure when `replace` was optimized, but `split`-`join` is now some 40+% slower. – Chiramisu Feb 16 '18 at 23:10
  • I do not understand why in Chrome 97, `replaceAll` (native) should be slower than `split().join()` in dealing with big strings? https://jsben.ch/AxKZ3 (Look fine in firefox) – MMMahdy-PAPION Jan 20 '22 at 05:09

14 Answers14

1305

The easiest would be to use a regular expression with g flag to replace all instances:

str.replace(/foo/g, "bar")

This will replace all occurrences of foo with bar in the string str. If you just have a string, you can convert it to a RegExp object like this:

var pattern = "foobar",
    re = new RegExp(pattern, "g");
Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
Gumbo
  • 643,351
  • 109
  • 780
  • 844
  • 2
    str.replace(/foo/g, "bar") caused an error for me. str.replace(/foo/, "bar") works. – Asmussen Feb 21 '12 at 22:16
  • 10
    Warning: This does not work for strings containing newlines. XRegExp has a replace method that does the trick. – kgriffs Jul 19 '12 at 15:44
  • 106
    my inner pedant is pointing out that the OP asked for the fastest, not the easiest – tomfumb Sep 20 '12 at 17:34
  • 6
    I did `user.email.replace(/./g,',')`, and the whole email got replaced with comas in the same number as characters in the email. Puzzled... – Jared Tomaszewski Sep 30 '13 at 20:13
  • @gumbo Please give me a good reference for **regular expression** in JS – Rajesh Paul Oct 14 '13 at 07:02
  • 34
    @JaredTomaszewski, the full stop (period) character in a regex stands for "any character". To signify an actual full stop, you'd need to precede it with a backslash i.e. user.email.replace(/\./g,',') – Squig Oct 23 '13 at 16:23
  • @Squig - what if I want to replace a `/`, like in here: `str.replace(/\//g,'\\/');` everything after the first `\` gets commented, because I get 2 `/` after each other... And even when I try with something simple, I get an error saying that `g` is undefined. – Syspect Dec 06 '13 at 16:12
  • @Syspect - I used `var newstr = str.replace(new RegExp('/', 'g'), 'replaced');` – David Apr 24 '15 at 04:30
  • I used this to replace a '~' with '\' for a loca image url, i.e. ItemImage = Item.ImageLocation.replace(/~/g, '\\'); – Doug J. Huras Nov 20 '15 at 19:24
  • 1
    Remember when trying to replace special characters (or characters used in native JS) to escape them as well. IE: `number = number.replace(/\+/g,"");` – Jacques Koekemoer Mar 15 '16 at 14:31
  • my inner meta pedant is thinking the OP's "fastest" could mean fastest to code, meaning the "easiest". ;-) – broc.seib May 15 '18 at 17:26
  • @Gumbo answer - user.email.replace(/foo/gi,"bar"); – Surya R Praveen Sep 26 '18 at 11:57
  • This routine is the fastest on Chrome but on firefox is 7 times slower than the best on firefox. Check my answer: https://stackoverflow.com/a/57697050/236062 – Zibri Aug 29 '19 at 11:26
  • The question was for the *fastest*, not the *easiest*. Results with https://jsperf.com/javascript-replace-all suggest that fastest is a custom function based on indexOf and string manipulation, however that may be dependent on the nature of the source strings. – Nick Nov 20 '19 at 10:54
  • my sting is var a = " hello , (hello , hello,) hello, (hello , hello,)" i want to replace ,) with ) how can i do that i was trying a = a.replace(/,)/g,')'); but it gives error of invalid regex – Kunal Burangi Jan 09 '20 at 05:44
162

Try this replaceAll: http://dumpsite.com/forum/index.php?topic=4.msg8#msg8

String.prototype.replaceAll = function(str1, str2, ignore) 
{
    return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
} 

It is very fast, and it will work for ALL these conditions that many others fail on:

"x".replaceAll("x", "xyz");
// xyz

"x".replaceAll("", "xyz");
// xyzxxyz

"aA".replaceAll("a", "b", true);
// bb

"Hello???".replaceAll("?", "!");
// Hello!!!

Let me know if you can break it, or you have something better, but make sure it can pass these 4 tests.

qwerty
  • 1,685
  • 1
  • 10
  • 2
  • 1
    This is quite good for replacing strings with unknown content, but his strings are fixed and does not need the complexity of escaping regular expressions. I upped this because I was searching for a replaceAll function. – NickSoft Nov 17 '13 at 16:06
  • 2
    @jens All I'm saying is a giant CAVEAT EMPTOR. RegExes are super complicated, and a random internet answer that is supposed to solve someone's problem once and for all, but which very, very likely has some lurking bugs, is a poor answer indeed. We don't expect unit tests on answers that are obviously correct—of course not. But we DO expect that answers give the reader a proper understanding of their risks when an inexperienced programmer is likely to be misled into false confidence. My comment helps warn those beginners against trusting this untested, crazy code. – ErikE Sep 07 '16 at 14:12
  • I've started adding unit tests for this function at the bottom of this test suite: https://github.com/agrothe/alphanumbers/blob/master/test/index.js – Andrew Grothe Sep 08 '16 at 12:47
  • 1
    The behavior of "x".replaceAll("", "xyz"); seems like a bug to me. If I tried to replace empty string I'd really expect an error. – Slater Victoroff Jan 06 '17 at 05:37
  • Slater -- that functionality comes from the behavior of split -- if you split on an empty string, an array of each of the characters is returned. Logically its as if there is an empty string between each character. It also matches a regex zero length match. The nitpicky among us might wonder why there is only one empty string found, and not an infinite amount.. – Gerard ONeill Jan 27 '17 at 18:15
  • This routine, for strings shorter than 500 characters is the slowest. Check my answer: https://stackoverflow.com/a/57697050/236062 – Zibri Aug 29 '19 at 10:49
  • 2
    Since there’s now a standard [`replaceAll`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll) method, this answer overrides it now. Please [do monkey patching correctly](/a/46491279/4642212) by checking the existence of a property first! – Sebastian Simon Mar 15 '21 at 00:04
  • An improved version of this idea (Without the smart idea of `i` `ignoreCase`): https://stackoverflow.com/a/70484568/7514010 – MMMahdy-PAPION Dec 30 '21 at 07:10
105
var mystring = 'This is a string';
var newString = mystring.replace(/i/g, "a");

newString now is 'Thas as a strang'

Sani Huttunen
  • 23,620
  • 6
  • 72
  • 79
  • 1
    This routine is the fastest on Firefox but very slow on chrome: check my answer: https://stackoverflow.com/a/57697050/236062 – Zibri Aug 29 '19 at 11:23
64

Also you can try:

string.split('foo').join('bar');
Vlada
  • 1,653
  • 19
  • 23
  • does this handle regular expression ? I guess. But for string to string replacement, this one is my favorite :) Very fast with firefox – yota Feb 23 '17 at 08:00
  • @yota Yes. You can use regular expression. `"12px (2) bar-456-foo 44".split(/\d/).join("#")` – Vlada Feb 24 '17 at 16:15
  • 2
    This is perfect for most simple cases. Works great in a nice little function like: `function replaceAll( s, f, r ){ return s.split( f ).join( r ); }`. Or if you think RegEx is faster: `function replaceAll( s, f, r ){ f = RegExp( f, 'gi' ); return s.replace( f, r ); }`. Then just do `foo = replaceAll( 'aaa', 'a', 'b' );`. – Beejor Jul 18 '17 at 02:59
  • 1
    The best answer – Juan Joya Jun 10 '19 at 19:16
  • If your needle is some variable that contains one or more regex reserved characters, then the methods of the other answers can do unexpected replacements. The good thing about this method is that it treats all characters the same way. – Quinten Jul 15 '21 at 09:36
  • Not necessarily ideal but quite brilliant. – code Aug 21 '22 at 04:34
20

You can use the following:

newStr = str.replace(/[^a-z0-9]/gi, '_');

or

newStr = str.replace(/[^a-zA-Z0-9]/g, '_');

This is going to replace all the character that are not letter or numbers to ('_'). Simple change the underscore value for whatever you want to replace it.

ssamuel68
  • 932
  • 13
  • 10
13

Use Regex object like this

var regex = new RegExp('"', 'g'); str = str.replace(regex, '\'');

It will replace all occurrence of " into '.

Neel Kamal
  • 221
  • 3
  • 2
13

Just thinking about it from a speed issue I believe the case sensitive example provided in the link above would be by far the fastest solution.

var token = "\r\n";
var newToken = " ";
var oldStr = "This is a test\r\nof the emergency broadcasting\r\nsystem.";
newStr = oldStr.split(token).join(newToken);

newStr would be "This is a test of the emergency broadcast system."

DamienS
  • 151
  • 1
  • 6
13

I think the real answer is that it completely depends on what your inputs look like. I created a JsFiddle to try a bunch of these and a couple of my own against various inputs. No matter how I look at the results, I see no clear winner.

  • RegExp wasn't the fastest in any of the test cases, but it wasn't bad either.
  • Split/Join approach seems fastest for sparse replacements.
  • This one I wrote seems fastest for small inputs and dense replacements:

    function replaceAllOneCharAtATime(inSource, inToReplace, inReplaceWith) {
        var output="";
        var firstReplaceCompareCharacter = inToReplace.charAt(0);
        var sourceLength = inSource.length;
        var replaceLengthMinusOne = inToReplace.length - 1;
        for(var i = 0; i < sourceLength; i++){
            var currentCharacter = inSource.charAt(i);
            var compareIndex = i;
            var replaceIndex = 0;
            var sourceCompareCharacter = currentCharacter;
            var replaceCompareCharacter = firstReplaceCompareCharacter;
            while(true){
                if(sourceCompareCharacter != replaceCompareCharacter){
                output += currentCharacter;
                break;
            }
            if(replaceIndex >= replaceLengthMinusOne) {
                i+=replaceLengthMinusOne;
                output += inReplaceWith;
                //was a match
                break;
            }
            compareIndex++; replaceIndex++;
            if(i >= sourceLength){
                // not a match
                break;
            }
            sourceCompareCharacter = inSource.charAt(compareIndex)
                replaceCompareCharacter = inToReplace.charAt(replaceIndex);
            }   
            replaceCompareCharacter += currentCharacter;
        }
        return output;
    }
    
Rick Velde
  • 581
  • 6
  • 9
  • 1
    Another consideration to factor in is that the split/join approach is the simplest, shortest, most to-the-point approach, making it the most likely candidate for future browsers to internally optimize it into something several times faster (e.g. instead of creating a new array and a new string, it would instead just search through & copy-n-paste it linearly similar to a regular expression) during JIST compilation. – Jack G Aug 03 '17 at 01:12
8

I just coded a benchmark and tested the first 3 answers. It seems that for short strings (<500 characters)
the third most voted answer is faster than the second most voted one.

For long strings (add ".repeat(300)" to the test string) the faster is answer 1 followed by the second and the third.

Note:

The above is true for browsers using v8 engine (chrome/chromium etc).
With firefox (SpiderMonkey engine) the results are totally different
Check for yourselves!! Firefox with the third solution seems to be
more than 4.5 times faster than Chrome with the first solution... crazy :D

function log(data) {
  document.getElementById("log").textContent += data + "\n";
}

benchmark = (() => {

  time_function = function(ms, f, num) {
    var z;
    var t = new Date().getTime();
    for (z = 0;
      ((new Date().getTime() - t) < ms); z++) f(num);
    return (z / ms)
  } // returns how many times the function was run in "ms" milliseconds.


  function benchmark() {
    function compare(a, b) {
      if (a[1] > b[1]) {
        return -1;
      }
      if (a[1] < b[1]) {
        return 1;
      }
      return 0;
    }

    // functions

    function replace1(s) {
      s.replace(/foo/g, "bar")
    }

String.prototype.replaceAll2 = function(_f, _r){ 

  var o = this.toString();
  var r = '';
  var s = o;
  var b = 0;
  var e = -1;
//      if(_c){ _f = _f.toLowerCase(); s = o.toLowerCase(); }

  while((e=s.indexOf(_f)) > -1)
  {
    r += o.substring(b, b+e) + _r;
    s = s.substring(e+_f.length, s.length);
    b += e+_f.length;
  }

  // Add Leftover
  if(s.length>0){ r+=o.substring(o.length-s.length, o.length); }

  // Return New String
  return r;
};

String.prototype.replaceAll = function(str1, str2, ignore) {
      return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, "\\$&"), (ignore ? "gi" : "g")), (typeof(str2) == "string") ? str2.replace(/\$/g, "$$$$") : str2);
    }

    function replace2(s) {
      s.replaceAll("foo", "bar")
    }

    function replace3(s) {
      s.split('foo').join('bar');
    }

    function replace4(s) {
      s.replaceAll2("foo", "bar")
    }


    funcs = [
      [replace1, 0],
      [replace2, 0],
      [replace3, 0],
      [replace4, 0]
    ];

    funcs.forEach((ff) => {
      console.log("Benchmarking: " + ff[0].name);
      ff[1] = time_function(2500, ff[0], "foOfoobarBaR barbarfoobarf00".repeat(10));
      console.log("Score: " + ff[1]);

    })
    return funcs.sort(compare);
  }

  return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();
console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
  log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>

The test will run for 10s (+2s) as you click the button.

My results (on the same pc):

Chrome/Linux Ubuntu 64:
1. replace1 score: 100% *winner* (766.18)
2. replace4 score: 99.07% speed of winner. (759.11)
3. replace3 score: 68.36% speed of winner. (523.83)
4. replace2 score: 59.35% speed of winner. (454.78)

Firefox/Linux Ubuntu 64
1. replace3 score: 100% *winner* (3480.1)
2. replace1 score: 13.06% speed of winner. (454.83)
3. replace4 score: 9.4% speed of winner. (327.42)
4. replace2 score: 4.81% speed of winner. (167.46)

Nice mess uh?

Took the liberty of adding more test results

Chrome/Windows 10
1. replace1 score: 100% *winner* (742.49)
2. replace4 score: 85.58% speed of winner. (635.44)
3. replace2 score: 54.42% speed of winner. (404.08)
4. replace3 score: 50.06% speed of winner. (371.73)

Firefox/Windows 10
1. replace3 score: 100% *winner* (2645.18)
2. replace1 score: 30.77% speed of winner. (814.18)
3. replace4 score: 22.3% speed of winner. (589.97)
4. replace2 score: 12.51% speed of winner. (331.13)

Edge/Windows 10
1. replace1 score: 100% *winner* (1251.24)
2. replace2 score: 46.63% speed of winner. (583.47)
3. replace3 score: 44.42% speed of winner. (555.92)
4. replace4 score: 20% speed of winner. (250.28)

Chrome on Galaxy Note 4

1. replace4 score: 100% *winner* (99.82)
2. replace1 score: 91.04% speed of winner. (90.88)
3. replace3 score: 70.27% speed of winner. (70.15)
4. replace2 score: 38.25% speed of winner. (38.18)
Zibri
  • 9,096
  • 3
  • 52
  • 44
7

What's the fastest I don't know, but I know what's the most readable - that what's shortest and simplest. Even if it's a little bit slower than other solution it's worth to use.

So use:

 "string".replace("a", "b");
 "string".replace(/abc?/g, "def");

And enjoy good code instead of faster (well... 1/100000 sec. is not a difference) and ugly one. ;)

Crozin
  • 43,890
  • 13
  • 88
  • 135
4

I tried a number of these suggestions after realizing that an implementation I had written of this probably close to 10 years ago actually didn't work completely (nasty production bug in an long-forgotten system, isn't that always the way?!)... what I noticed is that the ones I tried (I didn't try them all) had the same problem as mine, that is, they wouldn't replace EVERY occurrence, only the first, at least for my test case of getting "test....txt" down to "test.txt" by replacing ".." with "."... maybe I missed so regex situation? But I digress...

So, I rewrote my implementation as follows. It's pretty darned simple, although I suspect not the fastest but I also don't think the difference will matter with modern JS engines, unless you're doing this inside a tight loop of course, but that's always the case for anything...

function replaceSubstring(inSource, inToReplace, inReplaceWith) {

  var outString = inSource;
  while (true) {
    var idx = outString.indexOf(inToReplace);
    if (idx == -1) {
      break;
    }
    outString = outString.substring(0, idx) + inReplaceWith +
      outString.substring(idx + inToReplace.length);
  }
  return outString;

}

Hope that helps someone!

Frank W. Zammetti
  • 1,231
  • 1
  • 11
  • 19
3
// Find, Replace, Case
// i.e "Test to see if this works? (Yes|No)".replaceAll('(Yes|No)', 'Yes!');
// i.e.2 "Test to see if this works? (Yes|No)".replaceAll('(yes|no)', 'Yes!', true);
String.prototype.replaceAll = function(_f, _r, _c){ 

  var o = this.toString();
  var r = '';
  var s = o;
  var b = 0;
  var e = -1;
  if(_c){ _f = _f.toLowerCase(); s = o.toLowerCase(); }

  while((e=s.indexOf(_f)) > -1)
  {
    r += o.substring(b, b+e) + _r;
    s = s.substring(e+_f.length, s.length);
    b += e+_f.length;
  }

  // Add Leftover
  if(s.length>0){ r+=o.substring(o.length-s.length, o.length); }

  // Return New String
  return r;
};
MadHatter
  • 39
  • 2
  • This routine is the second fastest on Chrome but it takes 4.5 times the speed of fastest on Firefox, check my answer: https://stackoverflow.com/a/57697050/236062 – Zibri Aug 29 '19 at 11:20
1

Use the replace() method of the String object.

As mentioned in the selected answer, the /g flag should be used in the regex, in order to replace all instances of the substring in the string.

jmealy
  • 583
  • 1
  • 5
  • 14
Franci Penov
  • 74,861
  • 18
  • 132
  • 169
  • 42
    This only replaces the first occurence! – eddy147 Nov 04 '10 at 10:29
  • 4
    @Dr. Hfuhruhurr - it can also replace all matches, if the /g option is used, as specified by the `replace()` method documentation (http://www.w3schools.com/jsref/jsref_obj_regexp.asp for example). Just because I did not explicitly mention the `/g` option does not make my answer any less valid. – Franci Penov Nov 04 '10 at 16:15
0

@Gumbo adding extra answer - user.email.replace(/foo/gi,"bar");

/foo/g - Refers to the all string to replace matching the case sensitive

/foo/gi - Refers to the without case sensitive and replace all For Eg: (Foo, foo, FoO, fOO)

DEMO

Surya R Praveen
  • 3,393
  • 1
  • 24
  • 25