JavaScript str_replace
Replaces all occurrences of search in haystack with replace
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 | function str_replace (search, replace, subject, count) { // Replaces all occurrences of search in haystack with replace // // version: 1008.1718 // discuss at: http://phpjs.org/functions/str_replace // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + improved by: Gabriel Paderni // + improved by: Philip Peterson // + improved by: Simon Willison (http://simonwillison.net) // + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) // + bugfixed by: Anton Ongson // + input by: Onno Marsman // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + tweaked by: Onno Marsman // + input by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + input by: Oleg Eremeev // + improved by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Oleg Eremeev // % note 1: The count parameter must be passed as a string in order // % note 1: to find a global variable in which the result will be given // * example 1: str_replace(' ', '.', 'Kevin van Zonneveld'); // * returns 1: 'Kevin.van.Zonneveld' // * example 2: str_replace(['{name}', 'l'], ['hello', 'm'], '{name}, lars'); // * returns 2: 'hemmo, mars' var i = 0, j = 0, temp = '', repl = '', sl = 0, fl = 0, f = [].concat(search), r = [].concat(replace), s = subject, ra = r instanceof Array, sa = s instanceof Array; s = [].concat(s); if (count) { this.window[count] = 0; } for (i=0, sl=s.length; i < sl; i++) { if (s[i] === '') { continue; } for (j=0, fl=f.length; j < fl; j++) { temp = s[i]+''; repl = ra ? (r[j] !== undefined ? r[j] : '') : r[0]; s[i] = (temp).split(f[j]).join(repl); if (count && s[i] !== temp) { this.window[count] += (temp.length-s[i].length)/f[j].length;} } } return sa ? s : s[0]; } |
Examples
» Example 1
Running
1 | str_replace(' ', '.', 'Kevin van Zonneveld'); |
Should return
1 | 'Kevin.van.Zonneveld' |
» Example 2
Running
1 | str_replace(['{name}', 'l'], ['hello', 'm'], '{name}, lars'); |
Should return
1 | 'hemmo, mars' |
Dependencies
No dependencies, you can use this function standalone.
Open syntax issues
php.js uses JsLint to help us keep our code consistent and prevent some common bugs.
Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.
Authors
Thanks to the following developers, you get to have str_replace goodness in JavaScript.
Why do you use such a complicated function?
I have this one in use and it works fine:
function str_replace(search, replace, subject) {
return subject.split(search).join(replace);
}
@Erik: Just use the latest version here now; I recall the code had some buggy behavior, so it was fixed not too long ago.
One question: JSLint complains about
while (j = 0, i--) {
Problem at line 50 character 17: Expected ')' to match '(' from line 50 and instead saw ','.
Any idea how to fix this and the resulting errors in JSLint? Much appreciated!
Ouch! Thanks! I fixed that, and also had a bug in the next for loop initialization. Ok now in SVN... Thanks again...
@Oleg, as far as why the loop goes down, there is no good reason, except that I was kind of following the pattern that was there previously. :) I changed it to an ascending loop to follow convention and not raise the same question for others. I also optimized the for loops a little by checking for the length at the beginning of the loop instead of upon each iteration. Thanks!
Oh, sorry! I have read the documentation on php.net. I thought count param limits count of replaces, but it only counts replaces. I wrote a complete delusion, sorry. :)
Ok. Good job, the function is written very correctly. :)
But why:
[...for (i=s.length-1; i >= 0; i--) {...]
not:
[...for (i=0; i <= s.length-1; i++) {...]
??? String-array will be parsed from right to left? I don't know PHP function specification, but I think count param is used to count affected symbols from left to right, it's more useful. Am I right?
See my changes at http://trac.phpjs.org/projects/phpjs/browser/trunk/functions/strings/str_replace.js . I've tested all the PHP examples, and they are all working. Note that count must be in string form, and must out of necessity reference a global.
Hi Brett,
My code can convert extra items to empty strings too:[CODE]
function str_replace(search, replace, subject) {
var s = subject;
var f = [].concat(search);
var r = [].concat(replace);
var l = f.length;
var i = 0;
for (i=0; i<l; ++i)
{
s = s.split(f[i]).join((r[i]!=undefined)?r[i]:'');
}
return s;
}
[CODE]But I forgot about count param. It's more difficult. Anyway, I don't want it. :)
Hi Oleg,
Thanks for pointing out the bug... There was also a bug in handling the subject as an array. Should be fixed in SVN now. I think I might have also fixed the comments.
Btw, we can't just use the shorter length since the PHP behavior is to allow a longer search array to have extra items be replaced by the empty string (i.e., the extra items do get replaced (removed)).
I also added the count param...
In previous post something wrong.
function str_replace(search, replace, subject) {
var s = subject;
var f = [].concat(search);
var r = [].concat(replace);
var l = (f.length>r.length) ? r.length : f.length;
var i = 0;
for (i=0; i<l; ++i)
{
s = s.split(f[i]).join(r[i]);
}
return s;
}
Hello. I downloaded yesterday php.full.js. Library is very good, thank you for proceeding. But I tried to use str_replace function. It works not properly. Function replaces all items of search array with first item of replace array found in subject string. I wrote modified function:
function str_replace(search, replace, subject) {
var s = subject;
var f = [].concat(search);
var r = [].concat(replace);
var l = (f.length>r.length) ? r.length : f.length;
var i = 0;
for (i=0; i<l; ++i)
{
s = s.split(f[i]).join(r[i]);
}
return s;
}
This one do all I want. I hope it is useful, but I am not sure is it correct.
Sorry for my bad English, I am schoolboy from Latvia.
Thanks a lot for this blog. Is really usefull.
i'm from Barcelona and i'm a baby php programer and this php.js library look like so good.
note: my website is nearly in net.
hello and congratulations for this article(and script also). I needed something like str_replace from php very fast done with javscript and seems that your script does that very well!
Regards!
acimeha, yes, using the native JavaScript replace works fine for most cases, but this project aims to support PHP's behavior, which also allows arrays as inputs.
hi there !
consider this as solution for str_replace
str = str.replace(new RegExp("\n\r", 'gi'), "\n");
best regards
@ Onno Marsman & T. Wild: It's not my preferred coding style either, we may need to refactor it some day so it will be more consistent to other code found in php.js
No trouble at all! I guess you've misunderstood me. Like I said I also don't like that whole shorthand stuff. It's very hard to read indeed.
IN REPLY TO Onno Marsman.
As I said 'I'm not the best JavaScript programmer in the world.' and what I submitted worked for me (well, almost, r+='' should have been r = r+'' etc) in my limited tests, and I was having trouble seeing what the original code was doing anyway - I don't particularly like the shorthand ways of writing ifs etc - that's why I converted s,r and f to strings to be sure.
Your code fix works fine and sorry for the trouble.
T.Wild: I don't have time for testing right now but doesn't the following do the trick a bit better?
When you do pass an array you'd also want every element to be a string.
I don't see any need to convert r to string(s), but I could be wrong. I find the code a bit hard to read.
Why the whole variable name replacement thing anyway? I thought we have a compiler to do this. This only results in more code after compilation.
[CODE="Javascript"]
while (s[i] = (s[i]+'').split(f[j]).join(ra ? r[j] || "" : r[0]), ++j in f){};
[/CODE]
OOPS, I leaped before I looked at the code properly, this may be a better idea but I'm not the best JavaScript programmer in the world.
[CODE="Javascript"]
var f = search, r = replace, s = subject;
var ra = r instanceof Array, sa = s instanceof Array, f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;
if(!ra){r+='';}
if(!sa){s+='';}
if(!(f instanceof Array)){f+='';}
[/CODE]
I think the following line should should probably be changed from:
[CODE="Javascript"]
var f = search, r = replace, s = subject;
[/CODE]
TO
[CODE="Javascript"]
var f = search+'', r = replace+'', s = subject+'';
[/CODE]
Found this problem (if anyone is interested) when trying:
[CODE="Javascript"]
str_replace("foo","baa",location);
//You get the same problem with
str_replace(34,9,123456);
[/CODE]
Why be dependant on is_array when you could just use "instanceof Array" ? This probably saves a bit of performance too.
i have try to use this, but i five me error in FF 2.0.0.16 :
[CODE="text"]
s[i] has no properties
[Break on this error] while (s[i] = s[i].split(f[j])....in(ra ? r[j] || "" : r[0]), ++j in f){};
[/CODE]
so that i tried to fixed like this :
[CODE="Javascript"]
function str_replace(search, replace, subject) {
var f = search, r = replace, s = subject;
var ra = is_array(r), sa = is_array(s), f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;
while (j = 0, i--) {
if (s[i])
{
while (s[i] = s[i].split(f[j]).join(ra ? r[j] || "" : r[0]), ++j in f){};
};
};
return sa ? s : s[0];
}
[/CODE]
i have try to use this, but i five me error in FF 2 :
[CODE="text"]
s[i] has no properties
[Break on this error] while (s[i] = s[i].split(f[j])....in(ra ? r[j] || "" : r[0]), ++j in f){};
[/CODE]
so that i tried to fixed like this :
[CODE="Javascript"]
function str_replace(search, replace, subject) {
var f = search, r = replace, s = subject;
var ra = is_array(r), sa = is_array(s), f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;
while (j = 0, i--) {
if (s[i])
{
while (s[i] = s[i].split(f[j]).join(ra ? r[j] || "" : r[0]), ++j in f){};
}
};
return sa ? s : s[0];
}
[/CODE]
@ Jonas Raoni: That's astonishing :D The non-overlapping approach was convincing enough for me not to take the effort to test if this was the actual mechanism of PHP. It would make sense though right? Anyway, I will reinstate your version Jonas Raoni, and adjust the test case. Thanks for all of your work! You too Peter, some functions come easy, some tend to be a little of a pain. But that's the challenge & learning experience, right.
That is incredibly weird... I was certain that PHP replaced all the stuff at once so as not to create overlap... ah well, I guess it's not like that after all. It was a cool function though :P
I didn't looked the comments before, I just saw the code was long and made a version :)~
But it's strange... What php version are you using? The following code, on php 5, gave me as output "hemmo, mars":
echo str_replace(array('{name}', 'l'), array('hello', 'm'), '{name}, lars');
@ Jonas Raoni: I've updated, committed, generated, deployed, tested, reverted, commited, generated & deployed ;)
And srry dude but your str_replace did not survive Peter's testcase:
[CODE="Javascript"]
// should return: hello, mars
str_replace(['{name}', 'l'], ['hello', 'm'], '{name}, lars');
[/CODE]
More info in the comments here.
Guess I'll be needing those medals back ;) nah just kidding :)
@ Jonas Raoni: That looks very promising :) I still have to test it but if you have reduced our function to this, preserving all of it's functionality, then you deserve 3 medals ;)
I just made this code, it's using the already present is_array function.
function str_replace(f, r, s){
var ra = is_array(r), sa = is_array(s), f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;
while(j = 0, i--)
while(s[i] = s[i].split(f[j]).join(ra ? r[j] || "" : r[0]), ++j in f);
return sa ? s : s[0];
}
Example:
alert(str_replace(["a", "fb", "c"], ["f", "g"], ["abcd"]));
Hmm, I actually can see where that could come in useful though...
[CODE="Javascript"]
nsub = nsub.replace(new RegExp(__regexp_escape(search[x]), "g"), "[cod"+fincods[x]+"]");
could work with
nsub = nsub.split(search[x]).join("[cod"+fincods[x]+"]");
[/CODE]
and that sort of thing occurs in four places, so there's a significant drop in size, and you probably can even drop that __regexp_escape function [sorry Simon :(]. You probably should keep him in the credits though, even if you take the function out.
@ Jonas Raoni: Hi there, how have you been? Thats a brutal approach :) However the long code we have now supports some features that your approach does not. For more information see the comments here. Thanks for this insightful way though!
For the str_replace the best and fastest method I know it's:
"Jonas Raoni".split("a").join("X");
No problem!
Also: Credits to Simon Willison (http://simonwillison.net/2006/Jan/20/escape/) for the regexp_escape function.
@ Philip: A seemingly straightforward function like str_replace turns out to be more complex than e.g. mktime :) I've indented your code and included the extra function (had to 'var' it for namespacing compatibility) in str_replace.
Thanks A LOT for seriously improving this function!
Not necessarily; what if a result produced by the replacement of one of the shorter strings produced the values of one of the longer searches? Here's a code that works, but I haven't indented the part that I made yet, or commented it, and it requires a second external function... I dunno if you can fix that or not.
[CODE="Javascript"]
// http://kevin.vanzonneveld.net
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Gabriel Paderni
// + improved by: Philip Peterson
// * example 1: str_replace(' ', '.', 'Kevin van Zonneveld');
// * returns 1: 'Kevin.van.Zonneveld'
function regexp_escape(text) {
if (!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
}
function str_replace(search, replace, subject)
{
if(!(replace instanceof Array)){
replace=new Array(replace);
if(search instanceof Array){//If search is an array and replace is a string, then
this replacement string is used for every value of search
while(search.length>replace.length){
replace[replace.length]=replace[0];
}
}
}
if(!(search instanceof Array))search=new Array(search);
while(search.length>replace.length){//If replace has fewer values than search , then an
empty string is used for the rest of replacement values
replace[replace.length]='';
}
if(subject instanceof Array){//If subject is an array, then the search and replace is
performed with every entry of subject , and the return value is an array as well.
for(k in subject){
subject[k]=str_replace(search,replace,subject[k]);
}
return subject;
}
numreplx=search.length;
numon=0;
fincods=new Array();
while(fincods.length<numreplx)
{
nsub=subject;
for(x=0;x<fincods.length;x++)
{
nsub=nsub.replace(new RegExp(regexp_escape(search[x]), "g"), "[cod"+fincods[x]+"]");
}
for(x=0;x<fincods.length;x++)
{
nsub=nsub.replace(new RegExp(regexp_escape("[cod"+fincods[x]+"]"), "g"), replace[x]);
}
if(nsub.indexOf("[cod"+numon+"]") == -1)
{
fincods[fincods.length]=numon;
}
numon++;
}
for(x=0;x<fincods.length;x++)
{
subject=subject.replace(new RegExp(regexp_escape(search[x]), "g"), "[cod"+fincods[x]+"]");
}
for(x=0;x<fincods.length;x++)
{
subject=subject.replace(new RegExp(regexp_escape("[cod"+fincods[x]+"]"), "g"), replace[x]);
}
return subject;
}[/CODE]
@ Philip: Thanks for noticing. My first thought is to order the search array by length, ascending. This way 'l' will be searched & replaced before hello will be.
Do you agree that that would solve the issue?
There's only a slight problem with this code... in PHP, each entry is replaced all at once, rather than one after another. This creates a problem in the following example:
[CODE="Javascript"]
function parseInf(kyl){
kyl=str_replace([
"{name}",
"l"
],
[
"hello",
"m"
],
kyl);
nev=kyl.split(/\r{0,1}\n-\r{0,1}\n/);
return nev;
}
alert(parseInf("{name}, lars"));
[/CODE]
Theoretically, the code should return "hello, mars", but instead it returns "hemmo, mars". It completes the first iteration before the second, rather than all at once.


Kevin van Zonneveld
16 Aug '09