`String.prototype.matchAll`
É comum aplicar repetidamente a mesma expressão regular em uma string para obter todas as correspondências. Até certo ponto, isso já é possível hoje usando o método String#match
.
Neste exemplo, encontramos todas as palavras que consistem apenas em dígitos hexadecimais e, em seguida, registramos cada correspondência:
const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}
// Saída:
//
// 'DEADBEEF'
// 'CAFE'
No entanto, isso só fornece os substrings que correspondem. Normalmente, você não quer apenas os substrings, mas também informações adicionais, como o índice de cada substring ou os grupos de captura dentro de cada correspondência.
Já é possível alcançar isso escrevendo seu próprio loop e rastreando os objetos de correspondência você mesmo, mas é um pouco chato e não muito prático:
const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}
// Saída:
//
// [ 'DEADBEEF', índice: 19, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
// [ 'CAFE', índice: 28, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
A nova API String#matchAll
torna isso mais fácil do que nunca: agora você pode escrever um simples loop for
-of
para obter todos os objetos de correspondência.
const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}
// Saída:
//
// [ 'DEADBEEF', índice: 19, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
// [ 'CAFE', índice: 28, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
String#matchAll
é especialmente útil para expressões regulares com grupos de captura. Ele fornece todas as informações para cada correspondência individual, incluindo os grupos de captura.
const string = 'Repositórios favoritos do GitHub: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} em ${match.index} com '${match.input}'`);
console.log(`→ proprietário: ${match.groups.owner}`);
console.log(`→ repositório: ${match.groups.repo}`);
}
<!--truncate-->
// Saída:
//
// tc39/ecma262 em 23 com 'Repositórios favoritos do GitHub: tc39/ecma262 v8/v8.dev'
// → proprietário: tc39
// → repositório: ecma262
// v8/v8.dev em 36 com 'Repositórios favoritos do GitHub: tc39/ecma262 v8/v8.dev'
// → proprietário: v8
// → repositório: v8.dev
A ideia geral é que você escreve um simples loop for
-of
, e o String#matchAll
cuida do resto para você.
Nota: Como o nome implica, o String#matchAll
é destinado a iterar através de todos os objetos de correspondência. Como tal, ele deve ser usado com expressões regulares globais, ou seja, aquelas com o sinalizador g
, já que qualquer expressão regular não global produziria apenas uma única correspondência (no máximo). Chamar matchAll
com uma expressão regular não global resulta em uma exceção TypeError
.