Introduction:
Regular expressions (regex) serve as invaluable tools for pattern matching within strings, and JavaScript provides robust functionality for utilizing them. The RegExp.prototype.exec method has long been essential for extracting information from strings based on regex patterns. However, with the arrival of String.prototype.matchAll in ECMAScript 2020, the process of handling regular expressions has become significantly more user-friendly. In this article, we will delve into the String.prototype.matchAll method and examine how it streamlines the process of retrieving multiple matches from a string.
The Former Way: RegExp.prototype.exec
Before the implementation of String.prototype.matchAll, developers typically relied on the RegExp.prototype.exec method to iterate through a string and extract multiple matches. For instance, let’s examine a scenario where we aim to eliminate all instances of the word 'apple' from each string. In this case, the regular expression /\bapple\b/g is employed to locate the word 'apple' as a complete word (using word boundaries \b) along with the global flag g to identify all occurrences. The exec method is then applied within a loop to discover matches sequentially.
const text = ' An apple a day keeps the doctor away, but applesauce is delicious, too. ';
const pattern = /\bapple\b/g;
let match;
while (( match = pattern.exec(text)) !== null ) {
console.log(` Found ' ${match[0]} ' at index ${ match.index }`);
}
The New Methodology: String.prototype.matchAll
The String.prototype.matchAll function is designed to facilitate the extraction of all matches found within a string. It yields an iterator that includes all matched substrings along with their corresponding capturing groups. We will modify the previous example to incorporate matchAll. In this revised instance, text.matchAll(pattern) provides an iterator, which can be transformed into an array using the spread operator. Consequently, the resulting array comprises match objects, allowing us to effortlessly iterate through them to access the matched substrings alongside their respective indices.
const text = ' An apple a day keeps the doctor away, but applesauce is delicious, too. ';
const pattern = /\bapple\b/g;
const matches = [ ...text.matchAll( pattern )];
matches.forEach( match => {
console.log(` Found ' ${match[0]} ' at index ${ match.index }`);
});
Example:
// Sample text
const text = " Hello there! This is a sample text with some numbers like 123 and 456. ";
// Regular expression to find all numbers in the text
const regex = /\d+/g;
// Using match() to find all occurrences of the pattern
const result = text.match( regex );
// Displaying the output
console.log( result );
Output:
[ '123', '456' ]
In this instance, the regular expression \d+ is employed to identify one or more digits, while the g flag is used to locate every occurrence within the specified text. The match method yields an array that includes all the found matches. In this case, it produces ['123', '456'] as the output.
Improving with Catching Groups:
Utilizing capturing groups in regular expressions allows you to extract specific components from a match. The method String.prototype.matchAll leverages the extraction of these groups. Consider the following illustration. In this case, the pattern /(\w+ \w+) \((\d+)\)/g identifies names accompanied by their ages, which are enclosed in parentheses. The capturing groups can be easily accessed within the match object, enabling us to isolate and utilize particular pieces of information.
const text = ' Johnny Don (30) and Janey Doelle (28) are attending the party. ';
const pattern = /(\w+ \w+) \((\d+)\)/g;
const matches = [ ...text.matchAll(pattern )];
matches.forEach(match => {
const [ fullMatch, name, age ] = match;
console.log(` ${name} is ${age} years old. `);
});
Handling Non-Matching Groups
Occasionally, a regular expression may incorporate non-capturing groups (employing (?: ... )) to group elements without capturing them. The String.prototype.matchAll method retains these non-capturing groups in its results, enabling you to disregard them during processing:
const text = ' The prices are $10, €15, and £20. ';
const pattern = /(?:\$|€|£)(\d+)/g;
const matches = [ ...text.matchAll( pattern )];
matches.forEach( match => {
const price = match[1];
console.log(` Found price: ${price} `);
});
Conclusion:
The addition of String.prototype.matchAll has enhanced JavaScript's capabilities for handling regular expressions, facilitating the extraction of multiple matches from a string with greater ease. This method enhances clarity and reduces the need for explicit looping, resulting in more concise and expressive code. It is advisable to incorporate String.prototype.matchAll into your collection of regex tools for efficient and streamlined pattern matching in JavaScript.