69

I need some clarification for match Vs exec in JavaScript; here some one says that

"exec with a global regular expression is meant to be used in a loop" but first of all as you see in my example this is not the case; in my example exec with global regular expression is returning all of the matches in an array! Secondly they say that for String.match it returns all of the matches with no need of looping through! But again that's not happening in my example and it is just returning the input string? Have I misunderstood/done something wrong?

var myString = "[22].[44].[33].";
var myRegexp = /.*\[(\d*)*\].*\[(\d*)*\].*\[(\d*)*\].*/g;

var execResult = myRegexp.exec(myString);
console.log(execResult.length);
console.log(execResult[1]);// returns 22 and execResult has all of my matches from index 1 to the length of array


var matchResult = myString.match(myRegexp);
console.log(matchResult.length);
console.log(matchResult);// returns just myString which is "[22].[44].[33]."! Why is that?
Community
  • 1
  • 1

2 Answers2

107
  1. string.match finds the first match and returns it with the actual match, the index at which the text was found and the actual input, when the global flag is not used.

  2. string.match just returns all the matches, when the global flag is used.

var myString = "[22].[44].[33].";
    
console.log(myString.match(/\d+/)); 
// [ '22', index: 1, input: '[22].[44].[33].' ]
console.log(myString.match(/\d+/g));
// [ '22', '44', '33' ]

The main difference between string.match and regex.exec is, the regex object will be updated of the current match with regex.exec call. For example,

var myString = "[22].[44].[33].", myRegexp = /\d+/g, result;

while (result = myRegexp.exec(myString)) {
    console.log(result, myRegexp.lastIndex);
}

will return

[ '22', index: 1, input: '[22].[44].[33].' ] 3
[ '44', index: 6, input: '[22].[44].[33].' ] 8
[ '33', index: 11, input: '[22].[44].[33].' ] 13

As you can see, the lastIndex property is updated whenever a match is found. So, keep two things in mind when you use exec, or you will run into an infinite loop.

  1. If you don't use g option, then you will always get the first match, if there is one, otherwise null. So, the following will run into an infinite loop.

    var myString = "[22].[44].[33].", myRegexp = /\d+/, result;
    
    while (result = myRegexp.exec(myString)) {
        console.log(result, myRegexp.lastIndex);
    }
    
  2. Don't forget to use the same regular expression object with subsequent calls. Because, the regex object is updated every time, and if you pass new object, again the program will run into an infinite loop.

    var myString = "[22].[44].[33].", result;
    
    while (result = /\d+/g.exec(myString)) {
        console.log(result);
    }
    
Ivan
  • 34,531
  • 8
  • 55
  • 100
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
7

String.prototype.match() and RegExp.prototype.exec() are similar in both finding multiple occurrences and returning them in an array. Yet exec method returns an array of more detailed information. For instance unlike match it can find multiple occurrences of the capture groups as well. So if you have capture groups, exec is essential. One thing to keep in mind when working with exec you shouldn't invoke if from a literal regexp. Assign your regex to a variable first and use it to call your exec method from. One other thing is, while match would bring multiple occurrences in an array of items at one go, with exec you have to iterate for each occurrence to be captured.

Invoking match is fairly simple. Since it is a string prototype method you just chain it to a string and provide a regexp as an argument to the match method like; "test".match(/es/) A literal representation of a regex can be used with no problem.

Invoking exec is more complicated. As i mentioned previously it's best to have the regex assigned to something previously. Ok lets see an example

var text = '["job name 1","nat 1"],["job name 2","nat 2"],["job name 3","nat 3"]',
     reg = /([^"]+)","([^"]+)/g,
      tm = [],
      te = [];

tm = text.match(reg); // tm has result of match
while(te[te.length]=reg.exec(text)); // te has result of exec + an extra null item at the end
te.length--; // te normalized.

document.write("<pre>" + JSON.stringify(tm,null,2) + "</pre>\n");
document.write("<pre>" + JSON.stringify(te,null,2) + "</pre>\n");

As you see exec's result also includes the capture groups. The way i choose to populate te array is somewhat unorthodox but i hate to use a temp array just in the conditional part of while loop. This looks to me much more neat. The only thing is, the final null to stop the while loop gets inserted to the end of te array. Hence the following te.length-- instruction.

Edit: Now there is also the String.prototype.matchAll() functionality available in modern browsers which mostly lifts the burden of using exec over our shoulders. You may look another answer of mine to see that in action.

Redu
  • 25,060
  • 6
  • 56
  • 76
  • 6
    I believe it's misleading to say "So if you have capture groups, exec is essential", since `.match()` clearly does return an array of all specified captured groups. You imply as much in your first sentence and you obviously know how each works. Maybe you mean something more along the lines of "if you need to recursively apply one or more capture groups, using the global flag and `.exec()` are essential"? – ballenf Aug 31 '16 at 21:32
  • @Ben Fletcher Thanks. Yes right i was referring the case with getting the capture groups when global flag was set. However now we have the [String.prototype.matchAll()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll) which returns an iterator object that reveals all captured groups when spread. Mentioned as edit in above answer. – Redu May 26 '19 at 12:37
  • @Redu, I didn't get this part: "One thing to keep in mind when working with exec you shouldn't invoke if from a literal regexp. Assign your regex to a variable first and use it to call your exec method from." Why exactly you shouldn't invoke it from a literal regexp? – bp2017 Dec 26 '22 at 22:47
  • @bp2017 You can find the reason here under the [**Warning**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#sect1) section in pink. – Redu Dec 27 '22 at 19:02