15

So I have the following:

var token = '[token]';
var tokenValue = 'elephant';
var string = 'i have a beautiful [token] and i sold my [token]';
string = string.replace(token, tokenValue);

The above will only replace the first [token] and leave the second on alone.

If I were to use regex I could use it like

string = string.replace(/[token]/g, tokenValue);

And this would replace all my [tokens]

However I don't know how to do this without the use of //

Yves M.
  • 29,855
  • 23
  • 108
  • 144
Neta Meta
  • 4,001
  • 9
  • 42
  • 67
  • why does `//` make a difference? – Cfreak May 29 '13 at 01:10
  • // are used in regex to define limiters. If there is a way to add the g or "all" to replace without it what is the way ? – Neta Meta May 29 '13 at 01:11
  • The correct terminology for surrounding anything with `/ /` is that it makes a regex literal. The only ways I know of for doing replace all without a regex literal (or a third-party library) is to use the `RegExp` object constructor (which still uses a regex, see Isaac's answer), or to make a carefully constructed loop that will search for an occurrence of your `token` in the string and replace it with `tokenValue` until no occurrences of `token` are found. I suggest Isaac's answer if you are still able to use regex (not sure why you wouldn't be...). – ajp15243 May 29 '13 at 01:23
  • 1
    The reason i cannot use his answer is: http://jsfiddle.net/U43uM/1/ – Neta Meta May 29 '13 at 01:25

7 Answers7

20

I have found split/join satisfactory enough for most of my cases. A real-life example:

myText.split("\n").join('<br>');
pilat
  • 1,094
  • 1
  • 10
  • 17
  • what has this got to do with the question? – Isaac May 05 '22 at 21:49
  • @Isaac is this closer to the question? ``` var token = '[token]'; var tokenValue = 'elephant'; var string = 'i have a beautiful [token] and i sold my [token]'; string = string.split(token).join(tokenValue); ``` – pilat Jun 01 '22 at 06:56
9

Why not replace the token every time it appears with a do while loop?

var index = 0;
do {
    string = string.replace(token, tokenValue);
} while((index = string.indexOf(token, index + 1)) > -1);
Kerem
  • 11,377
  • 5
  • 59
  • 58
TheBestGuest
  • 142
  • 3
  • Yours seems to be what i am looking for however doesnt indexOF has some browser incompatibility ? – Neta Meta May 29 '13 at 01:31
  • You would have problems in older IE versions if you used [`Array.indexOf`](http://msdn.microsoft.com/en-us/library/ie/ff679977(v=vs.94).aspx). However, [`String.indexOf`](http://msdn.microsoft.com/en-us/library/ie/53xtt423(v=vs.94).aspx) is supported, so you should have no problems using it on strings. – ajp15243 May 29 '13 at 01:56
  • 2
    this will not work: `var strinSample= 'CIC'; var index = 0; do { strinSample = strinSample.replace('C', 'CC'); } while((index = strinSample.indexOf('C', index + 1)) > -1);` – Devid Sep 21 '17 at 16:03
  • Yes the problem is it searches from the beginning of the string every time. – Timmmm Feb 20 '20 at 06:39
2
string = string.replace(new RegExp("\\[token\\]","g"), tokenValue);
Isaac
  • 11,409
  • 5
  • 33
  • 45
  • issac your answer is nice but does not solve my issue and the "[]" are not being replaced - also i am unable to escape the "[]". – Neta Meta May 29 '13 at 01:26
  • No cannot and even if i escape them there is still an issue. – Neta Meta May 29 '13 at 01:31
  • After some fiddling, and looking at http://stackoverflow.com/questions/6863050/how-can-i-put-square-brackets-in-regexp-javascript I figured out how to escape `[]` – Isaac May 29 '13 at 01:42
  • You're missing i am unable to escape or change the value of the variable token Thanks for your help though – Neta Meta May 29 '13 at 01:57
  • 1
    @NetaMeta Now it makes sense, you need to escape `[ ]` but can't change the original value of the `token` regex string. You could use the answers [here](http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex) or [here](http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript) to replace characters in the `token` string with their equivalent regex metacharacters. Then you can use `token` as a regex where you please. Note that this would be a bad solution if a user inputs the regex, as they could put in something like `[`. – ajp15243 May 29 '13 at 02:03
1

Caution with the accepted answer, the replaceWith string can contain the inToReplace string, in which case there will be an infinite loop...

Here a better version:

function replaceSubstring(inSource, inToReplace, inReplaceWith)
{
    var outString = [];
    var repLen = inToReplace.length;

    while (true)
    {
        var idx = inSource.indexOf(inToReplace);
        if (idx == -1)
        {
            outString.push(inSource);
            break;
        }

        outString.push(inSource.substring(0, idx))
        outString.push(inReplaceWith);

        inSource = inSource.substring(idx + repLen);
    }

    return outString.join("");
}
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • 1
    In case you don't like putting `while(true)` in your code, here's a version with explicit condition: https://gist.github.com/naugtur/1d2227b09601e50dc9e0 – naugtur Feb 22 '16 at 10:35
  • It's much faster to append to a string than to make an array of parts and `.join("")` them. See my answer. – Timmmm Feb 20 '20 at 09:24
  • @Timmmm: To the best of my knowledge, that's not the case. I didn't measure this specific snippet, but usually, join is faster than concat. Did you actually measure it ? Which browser ? Which version ? – Stefan Steiger Feb 20 '20 at 10:36
  • Yes I did. Chrome as of now. It's 4 times slower. – Timmmm Feb 20 '20 at 17:14
  • @Timmmm: Hmmm, used to be 100 times faster. But apparently, recent changes. Wondering about the performance of var result = String.prototype.concat.apply("", arrayOfStrings); – Stefan Steiger Feb 20 '20 at 17:56
1

Unfortunately since Javascript's string replace() function doesn't let you start from a particular index, and there is no way to do in-place modifications to strings it is really hard to do this as efficiently as you could in saner languages.

  • .split().join() isn't a good solution because it involves the creation of a load of strings (although I suspect V8 does some dark magic to optimise this).
  • Calling replace() in a loop is a terrible solution because replace starts its search from the beginning of the string every time. This is going to lead to O(N^2) behaviour! It also has issues with infinite loops as noted in the answers here.
  • A regex is probably the best solution if your replacement string is a compile time constant, but if it isn't then you can't really use it. You should absolutely not try and convert an arbitrary string into a regex by escaping things.

One reasonable approach is to build up a new string with the appropriate replacements:

function replaceAll(input: string, from: string, to: string): string {
  const fromLen = from.length;
  let output = "";
  let pos = 0;
  for (;;) {
    let matchPos = input.indexOf(from, pos);
    if (matchPos === -1) {
      output += input.slice(pos);
      break;
    }
    output += input.slice(pos, matchPos);
    output += to;
    pos = matchPos + fromLen;
  }
  return output;
}

I benchmarked this against all the other solutions (except calling replace() in a loop which is going to be terrible) and it came out slightly faster than a regex, and about twice as fast as split/join.

Edit: This is almost the same method as Stefan Steiger's answer which I totally missed for some reason. However his answer still uses .join() for some reason which makes it 4 times slower than mine.

Timmmm
  • 88,195
  • 71
  • 364
  • 509
0
"[.token.*] nonsense and [.token.*] more nonsense".replace("[.token.*]", "some", "g");

Will produce:

"some nonsense and some more nonsense"

  • 1
    Please note that this only works on [Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) – William Barbosa Nov 13 '14 at 17:25
  • 1
    The flags parameter has been removed from Firefox now so this doesn't work anywhere. – Timmmm Feb 20 '20 at 09:20
0

I realized that the answer from @TheBestGuest won't work for the following example as you will end up in an endless loop:

var stringSample= 'CIC'; 
var index = 0; 
do { stringSample = stringSample.replace('C', 'CC'); } 
while((index = stringSample.indexOf('C', index + 1)) > -1);

So here is my proposition for replaceAll method written in TypeScript:

let matchString = 'CIC';
let searchValueString= 'C';
let replacementString ='CC';
    
matchString = matchString.split(searchValueString).join(replacementString);
     console.log(matchString);
Yves M.
  • 29,855
  • 23
  • 108
  • 144
Devid
  • 1,823
  • 4
  • 29
  • 48