12

I'm trying to replace part of a string with the same number of dummy characters in JavaScript, for example: '==Hello==' with '==~~~~~=='.

This question has been answered using Perl and PHP, but I can't get it to work in JavaScript. I've been trying this:

txt=txt.replace(/(==)([^=]+)(==)/g, "$1"+Array("$2".length + 1).join('~')+"$3");

The pattern match works fine, but the replacement does not - the second part adds '~~' instead of the length of the pattern match. Putting the "$2" inside the parentheses doesn't work. What can I do to make it insert the right number of characters?

Community
  • 1
  • 1
hamboy
  • 727
  • 2
  • 8
  • 21
  • Related/dupe for pure regex: http://stackoverflow.com/questions/7337995/regex-replace-sequence-of-one-character-with-same-number-of-another-character – blahdiblah Apr 27 '17 at 19:59

4 Answers4

13

Use a function for replacement instead:

var txt = "==Hello==";
txt = txt.replace(/(==)([^=]+)(==)/g, function ($0, $1, $2, $3) {
    return $1 + (new Array($2.length + 1).join("~")) + $3;
});

alert(txt);
//-> "==~~~~~=="
Andy E
  • 338,112
  • 86
  • 474
  • 445
3

The issue with the expression

txt.replace(/(==)([^=]+)(==)/g, "$1"+Array("$2".length + 1).join('~')+"$3") 

is that "$2".length forces $2 to be taken as a string literal, namely the string "$2", that has length 2.

From the MDN docs:

Because we want to further transform the result of the match before the final substitution is made, we must use a function.

This forces evaluation of the match before the transformation.

With an inline function as parameter (and repeat) -- here $1, $2, $3 are local variables:

txt.replace(/(==)([^=]+)(==)/g, (_,$1,$2,$3) => $1+'~'.repeat($2.length)+$3);

txt = '==Hello==';

//inline function
console.log(
  txt.replace(/(==)([^=]+)(==)/g, (_, g1, g2, g3) => g1 + '~'.repeat(g2.length) + g3)
);
user2314737
  • 27,088
  • 20
  • 102
  • 114
1

The length attribute is being evaluated before the $2 substitution so replace() won't work. The function call suggested by Augustus should work, another approach would be using match() instead of replace().

Using match() without the /g, returns an array of match results which can be joined as you expect.

txt="==Hello==";
mat=txt.match(/(==)([^=]+)(==)/);  // mat is now ["==Hello==","==","Hello","=="]
txt=mat[1]+Array(mat[2].length+1).join("~")+mat[3]; // txt is now "==~~~~~=="

You excluded the leading/trailing character from the middle expression, but if you want more flexibility you could use this and handle anything bracketed by the leading/trailing literals.

mat=txt.match(/(^==)(.+)(==$)/)
Joe Skora
  • 14,735
  • 5
  • 36
  • 39
0

A working sample uses the following fragment:

var processed = original.replace(/(==)([^=]+)(==)/g, function(all, before, gone, after){
    return before+Array(gone.length+1).join('~')+after;
});

The problem in your code was that you always measured the length of "$2" (always a string with two characters). By having the function you can measure the length of the matched part. See the documentation on replace for further examples.

Augustus Kling
  • 3,303
  • 1
  • 22
  • 25
  • Your code has a slight error in it and will return one character less than expected. This is because the array length needs to be 1 more than the string length for `join()` to produce a replacement string of equal length. – Andy E Sep 17 '11 at 21:45