Get all regex matches with capturing groups in JavaScript
By Flavio Copes
Learn how to get every match of a regex in JavaScript with matchAll() and the g flag, and read named capturing groups from each result using match.groups.
To get all the matches of a regular expression in JavaScript, including their capturing groups, use the string matchAll() method with a regex that has the g flag. Each result gives you the full match, the groups, and the position in the string.
I ran into this while processing a string that contained multiple URLs. I wanted to extract parts of each URL using a regular expression with capturing groups, which are great.
Tip: you can test regexes with capturing groups in my regex tester, it lists every group of every match.
So, let’s start from getting one single result:
const text = 'hello1 bla bla hello2'
const regex = /hello\d/
text.match(regex)
/*
[ 'hello1', index: 0, input: 'hello1 bla bla hello2', groups: undefined ]
*/
Getting multiple matches
Getting multiple results from a regex can be done using the g flag and it’s automatic, but now the result of match() is different, just the matched result:
const text = 'hello1 bla bla hello2'
const regex = /hello\d/g
console.log(text.match(regex))
//[ 'hello1', 'hello2' ]
Notice we lost the index and groups information. With the g flag, match() flattens everything into an array of strings.
Using matchAll() (an ES2020 feature) you can get a more detailed result set.
That returns an iterator object, so you need to use a loop to go through the results:
for (let match of text.matchAll(regex)) {
console.log(match)
}
/*
[ 'hello1', index: 0, input: 'hello1 bla bla hello2', groups: undefined ]
[ 'hello2', index: 15, input: 'hello1 bla bla hello2', groups: undefined ]
*/
If you prefer working with an array, spread the iterator:
const matches = [...text.matchAll(regex)]
How do you read the capturing groups?
Let’s talk about capturing groups.
Say you have a text containing dates like this:
const text = '2015-01-02 2022-02-04 2040-12-02'
and you have a regex to match that date pattern, because maybe you want to get the years references:
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g
This uses named capturing groups.
Using text.match(regex) now will not give you any information about the groups:
const text = '2015-01-02 2022-02-04 2040-12-02'
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g
text.match(regex)
/*
[ '2015-01-02', '2022-02-04', '2040-12-02' ]
*/
but you can get this information using text.matchAll(regex):
const text = '2015-01-02 2022-02-04 2040-12-02'
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g
for (let match of text.matchAll(regex)) {
console.log(match)
}
/*
[ '2015-01-02', '2015', '01', '02', index: 0, input: '2015-01-02 2022-02-04 2040-12-02', groups: [Object: null prototype] { year: '2015', month: '01', day: '02' } ]
[ '2022-02-04', '2022', '02', '04', index: 11, input: '2015-01-02 2022-02-04 2040-12-02', groups: [Object: null prototype] { year: '2022', month: '02', day: '04' } ]
[ '2040-12-02', '2040', '12', '02', index: 22, input: '2015-01-02 2022-02-04 2040-12-02', groups: [Object: null prototype] { year: '2040', month: '12', day: '02' } ]
*/
Each match is an array. Position 0 holds the full match, then each capturing group follows in order: match[1] is the year, match[2] the month, match[3] the day. Named groups also end up in the groups object, which reads much better.
So you could extract the year information like this:
const text = '2015-01-02 2022-02-04 2040-12-02'
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g
for (let match of text.matchAll(regex)) {
console.log(match.groups.year)
}
/*
'2015'
'2022'
'2040'
*/
Watch out for the g flag
One thing that will bite you: matchAll() requires the g flag. If the regex doesn’t have it, you get an error:
TypeError: String.prototype.matchAll called with a non-global RegExp argument
So if matchAll() throws, check the flags on your regex first.
Related posts about js: