0

I'm working in a problem which require replace negative regex.

If I have a string text, a regex pattern re and a transform function f

I want to have a negativeReplace function which replace all don't match sub strings with re pattern in text string by apply transform function f, and remain the rest.

# input
var text = " aa ( bb ) cc ";
var re = /[\(].*?[\)]/g;
var f = function(s){ return s.toUpperCase() }
# expected output
negativeReplace(text, re, f)
# -> " AA ( bb ) CC "

Here is my best try so far

function negativeReplace(text, re, f){
  var output = ""
  var boundary = 0;
  while((match = re.exec(text)) != null){
    output += f(text.slice(boundary, match.index)) + match[0];
    boundary = match.index + match[0].length;
  }
  output += f(text.slice(boundary, text.length));
  return output;
}

It worked!

function negativeReplace(text, re, f){
  var output = ""
  var boundary = 0;
  while((match = re.exec(text)) != null){
    output += f(text.slice(boundary, match.index)) + match[0];
    boundary = match.index + match[0].length;
  }
  output += f(text.slice(boundary, text.length));
  return output;
}

var text1 = " aa ( bb ) cc ";
var text2 = " aa { bb } cc { dd } ee ";

var f1 = function(s){ return s.toUpperCase() }
var f2 = function(s){ return "_" + s + "_" }

re = /[\({].*?[\)}]/g

console.log(negativeReplace(text1, re, f1)) // AA ( bb ) CC 
console.log(negativeReplace(text2, re, f1)) // AA { bb } CC { dd } EE 
console.log(negativeReplace(text1, re, f2)) // _ aa _( bb )_ cc _
console.log(negativeReplace(text2, re, f2)) // _ aa _{ bb }_ cc _{ dd }_ ee 

However, my implement seems too complicated. Because javascript already has replace function with matched pattern. And with simple case of negative regex like replace with a blank all characters except numbers, there is solution in this post.

So my question is how can I solve this problem better (can I use replace for this, how can I improve my code).

Thank you so much.

Vu Anh
  • 955
  • 1
  • 18
  • 29
  • The linked post isn't really a "negative" regex, in that the solution uses regexes that positively match individual characters to be replaced. Your requirement isn't really in the same category. – nnnnnn Aug 04 '17 at 02:41
  • Do you need to do this for *that specific expression*, or for *any* expression? And you're currently replacing parts of the string that *do* match the expression, so it's not clear what you're actually trying to do here. – T.J. Crowder Aug 04 '17 at 02:41
  • @T.J.Crowder In my problem, I must use this function many times. *That specific expression* is just a simple test case for describe my problem. I **do** replace all substring that **don't match** expression. Please look more carefully to my code and It worked with some test cases as I wrote and this post. Thank you so much – Vu Anh Aug 04 '17 at 02:48

1 Answers1

1

I can't think of a way to do it with just .replace(), but I can think of a way to do it in one statement using .split(), .map(), and .join().

If the regex you use with .split() has capturing parentheses then the matched text will be included in the resulting array, so basically all the array elements with even indices will be the bits that didn't match the regex, and all the elements with odd indices will be the bits that matched the regex. So:

function negativeReplace(str, re, f) {
  return str
    .split(re)
    .map(function(v, i) { return i % 2 === 0 && v ? f(v) : v })
    .join("")
}

var text1 = " aa ( bb ) cc ";
var text2 = " aa { bb } cc { dd } ee ";
var text3 = " aa { bb }{ dd } ee "; // example with no text between two matching substrings

var f1 = function(s){ return s.toUpperCase() }
var f2 = function(s){ return "_" + s + "_" }

re = /([\({].*?[\)}])/        // <--- Note the added parens

console.log(negativeReplace(text1, re, f1)) // AA ( bb ) CC 
console.log(negativeReplace(text2, re, f1)) // AA { bb } CC { dd } EE 
console.log(negativeReplace(text1, re, f2)) // _ aa _( bb )_ cc _
console.log(negativeReplace(text2, re, f2)) // _ aa _{ bb }_ cc _{ dd }_ ee _
console.log(negativeReplace(text3, re, f2)) // _ aa _{ bb }{ dd }_ ee _

EDIT: Note that the i % 2 === 0 && v test includes && v because if the regex matches two adjacent substrings then .split() will return an empty string for the text between those two matches, and I've assumed you don't want to call your transformation function on these empty strings. Obviously if you do want to transform empty strings (such that, say, your f2() function would insert "__" in the text3 example), then simply remove the && v part.

nnnnnn
  • 147,572
  • 30
  • 200
  • 241