2

I have a RegExp, doing a string replace, with global set. I only need one replace, but I'm using global because there's a second set of pattern matching (a mathematical equation that determines acceptable indices for the start of the replace) that I can't readily express as part of a regex.

var myString = //function-created string
myString = myString.replace(myRegex, function(){
    if (/* this index is okay */){

        //!! want to STOP searching now !!//
        return //my return string

    } else {
        return arguments[0];
        //return the string we matched (no change)
        //continue on to the next match
    }
}, "g");

If even possible, how do I break out of the string global search?

Thanks

Possible Solution

A solution (that doesn't work in my scenario for performance reasons, since I have very large strings with thousands of possible matches to very complex RegExp running hundreds or thousands of times):

var matched = false;
var myString = //function-created string
myString = myString.replace(myRegex, function(){
    if (!matched && /* this index is okay */){
        matched = true;
        //!! want to STOP searching now !!//
        return //my return string

    } else {
        return arguments[0];
        //return the string we matched (no change)
        //continue on to the next match
    }
}, "g");
Randy Hall
  • 7,716
  • 16
  • 73
  • 151
  • 1
    I'am somehow missing your regex and a simple input and expected output sample – rene Jan 29 '14 at 15:56
  • Can you just `match` them first, and just loop through those? – Wrikken Jan 29 '14 at 15:56
  • @Wrikken Technically that might work, but it's a performance hit. I added a possible solution, which just matches everything regardless (similar to what you're saying) however the performance hit in my scenario is pretty harsh. – Randy Hall Jan 29 '14 at 15:58
  • 2
    @RandyHall: If the performance is a problem, then perhaps you need to rethink the way you are doing this. The global option will match every possible match, that's what it's for, there is no way to exit early. If you only need to match once, don't use `g`. So perhaps you'd be better off figuring out why you feel you need `g` and whether there's a better way to handle that. – Matt Burland Jan 29 '14 at 16:02
  • 2
    If you even want to stop parsing the rest of the string for matches, you write that parser and spoonfeed it incrementing portions of your input, and stop parsing when you've found it. Or you can omit the `g`, and [use the match.index](http://stackoverflow.com/questions/2295657/return-positions-of-a-regex-match-in-javascript) to remove matches from the input one by one if they don't comply with your mathematical check until you find one that does. – Wrikken Jan 29 '14 at 16:04
  • @MattBurland I have a second method ready that doesn't use `g`, however this seemed like the best method *if* it was possible. If you're sure it's not, write an answer to that affect. If no one answers otherwise I'll accept it. – Randy Hall Jan 29 '14 at 16:07
  • @Wrikken good example. It may be even more efficient than the other method I was considering. I'm still hopeful this is possible, but it's looking like that's a negative. – Randy Hall Jan 29 '14 at 16:10

3 Answers3

3

Use RegExp.exec() instead. Since you only do replacement once, I make use of that fact to simplify the replacement logic.

var myString = "some string";
// NOTE: The g flag is important!
var myRegex = /some_regex/g;

// Default value when no match is found
var result = myString;
var arr = null;

while ((arr = myRegex.exec(myString)) != null) {
    // arr.index gives the starting index of the match
    if (/* index is OK */) {
        // Assign new value to result
        result = myString.substring(0, arr.index) +
                 /* replacement */ +
                 myString.substring(myRegex.lastIndex);
        break;
    }

    // Increment lastIndex of myRegex if the regex matches an empty string
    // This is important to prevent infinite loop
    if (arr[0].length == 0) {
        myRegex.lastIndex++;
    }
}

This code exhibits the same behavior as String.match(), since it also increments the index by 1 if the last match is empty to prevent infinite loop.

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
1

You can put try-catch and use undeclared variable to exit the replace function

var i = 0;

try{
 "aaaaa".replace ( /./g, function( a, b ){

  //Exit the loop on the 3-rd iteration
  if ( i === 3 ){

   stop; //undeclared variable

  }

  //Increment i
  i++

 })

}
catch( err ){
}

alert ( "i = " + i ); //Shows 3
Didakov
  • 35
  • 4
0

I question your logic about performance. I think some points made in the comments are valid. But, what do I know... ;)

However, this is one way of doing what you want. Again, I think this, performance wise, isn't the best...:

var myString = "This is the original string. Let's see if the original will change...";
var myRegex = new RegExp('original', 'g');
var matched=false;

document.write(myString+'<br>');

myString = myString.replace(myRegex, function (match) {

    if ( !matched ) {

        matched = true;
        return 'replaced';

    } else {
        return match;
    }
});

document.write(myString);

It's pretty much like your "Possible Solution". And it doesn't "abort" after the replace (hence my performance reservation). But it does what you asked for. It replaces the first instance, sets a flag and after that just returns the matched string.

See it work here.

Regards.

SamWhan
  • 8,296
  • 1
  • 18
  • 45