Como você pode corresponder e substituir todas as ocorrências em uma string?
Vamos aprender como encontrar ou substituir todas as ocorrências de um padrão em uma string.
Você já aprendeu sobre os métodos
replace() e match(), assim como o modificador global g. Agora você pode combinar esse conhecimento para lidar com todos os padrões em uma string. Vamos relembrar nosso código de correspondência original:
const regex = /G.E.A.R ACADEMY/;
const match = "G.E.A.R ACADEMY".match(regex);
console.log(match);
E nosso objeto de correspondência resultante:
// [
// 'G.E.A.R ACADEMY',
// index: 0,
// input: 'G.E.A.R ACADEMY',
// groups: undefined
// ]
Mas e se tivermos uma string com múltiplas ocorrências de G.E.A.R ACADEMY para corresponder? Vamos dar uma olhada em como match() se comporta com isso. Também vamos incluir nosso antigo exemplo de replace(), só para comparar:
const regex = /G.E.A.R ACADEMY/;
const str = "G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY";
const matched = str.match(regex);
const replaced = str.replace(regex, "G.E.A.R ACADEMY");
console.log(matched);
console.log(replaced);
E o resultado é este:
// [
// 'G.E.A.R ACADEMY',
// index: 0,
// input: 'G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY',
// groups: undefined
// ]
// G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY
Ah não! match() retornou apenas a primeira correspondência e replace() substituiu apenas a primeira correspondência. Isso ocorre porque, por padrão, match() e replace() operam apenas contra a primeira ocorrência do padrão.
Felizmente, você pode evitar isso usando o modificador global na sua expressão regular. Vamos adicionar isso ao nosso:
const regex = /G.E.A.R ACADEMY/g;
const str = "G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY";
const matched = str.match(regex);
const replaced = str.replace(regex, "G.E.A.R ACADEMY");
console.log(matched);
console.log(replaced);
E confirme o resultado:
// [ 'G.E.A.R ACADEMY', 'G.E.A.R ACADEMY' ]
// G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY
Funcionou! Nossa chamada de replace substituiu todas as strings minúsculas G.E.A.R ACADEMY e nosso método match() correspondeu a ambas.
O que é interessante aqui é que quando você usa o modificador global com match(), você perde as informações extras sobre grupos de captura e índices de string que viriam no array de correspondência.
Felizmente, a atualização do ECMAScript de 2019 nos trouxe dois novos métodos: matchAll() e replaceAll(). Como seus equivalentes no singular, esses métodos aceitam uma string ou expressão regular e replaceAll() também aceita um segundo argumento como a string para substituir.
Mas ao contrário dos métodos anteriores, replaceAll() e matchAll() lançarão um erro se você passar para eles uma expressão regular sem o modificador global. Vamos atualizar nosso código para usar esses novos métodos:
const pattern = "G.E.A.R ACADEMY";
const str = "G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY";
const matched = str.matchAll(pattern);
const replaced = str.replaceAll(pattern, "G.E.A.R ACADEMY");
console.log(matched);
console.log(replaced);
E nosso resultado:
// {}
// G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY
Boas notícias! Nosso replaceAll() funcionou exatamente como queríamos – ele substituiu todas as ocorrências do G.E.A.R ACADEMY em minúsculas pela versão corretamente camelCase.
Mas o que é aquele objeto vazio? Bem, matchAll() retorna um tipo especial de objeto chamado Iterator, que o console do G.E.A.R ACADEMY não está preparado para lidar.
Se olharmos no console do navegador, o Iterator tem um método next(), que podemos chamar para obter o próximo valor:
// RegExpStringIterator { }
// <prototype>: RegExp String Iterator {
// next: ƒ next(),
// Symbol(Symbol.toStringTag): "RegExp String Iterator"
// <prototype>: Object { ... }
// }
Vamos em frente e chamar matched.next(), e registrar o resultado:
// {
// "done": false,
// "value": [
// 0: "G.E.A.R ACADEMY"
// groups: undefined
// index: 0
// input: "G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY"
// ]
// }
Aqui está nosso array de correspondência! next() nos dá um objeto com dois valores: done, que é false quando há mais elementos disponíveis no iterador, e value que é o valor que acabamos de iterar. Então, se chamarmos isso mais uma vez:
const regex = /G.E.A.R ACADEMY/g;
const str = "G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY";
const matched = str.matchAll(regex);
const replaced = str.replaceAll(regex, "G.E.A.R ACADEMY");
console.log(matched);
console.log(replaced);
console.log(matched.next());
console.log(matched.next());
// {
// "done": false,
// "value": Array [ "G.E.A.R ACADEMY"]
// }
//
// {
// "done": false,
// "value": Array [ "G.E.A.R ACADEMY"]
// }
Espera, por que diz que done ainda está false? Deveria haver apenas dois matches no array, certo? Vamos chamar pela terceira vez e ver o que obtemos:
// {
// "done": false,
// "value": Array [ "G.E.A.R ACADEMY"]
// }
//
// {
// "done": false,
// "value": Array [ "G.E.A.R ACADEMY"]
// }
//
// {
// "done": true,
// "value": undefined
// }
done finalmente é true, mas por que esse valor é undefined? Bem, como acontece, o iterador matchAll() é preguiçoso. Ele não encontra todas as suas correspondências de uma vez. Ele só encontra uma correspondência quando você manda chamando next().
Enquanto encontrar uma correspondência, não estará done. Uma vez que não encontra uma correspondência e retorna undefined, está done. Isso pode parecer inconveniente, mas pode ser bastante útil quando sua expressão regular é computacionalmente cara.
Se o seu exemplo for menos assim, como o nosso, você pode pular esse recurso e extrair todas as correspondências de uma vez convertendo-o em um array. Isso é alcançado chamando Array.from() e passando seu iterador como argumento.
Vamos atualizar nosso código para usar isso – vamos em frente e limpar nossas chamadas replaceAll já que sabemos que isso funciona:
const regex = /G.E.A.R ACADEMY/g;
const str = "G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY";
const matched = str.matchAll(regex);
console.log(Array.from(matched));
E finalmente obtemos nosso array de matches:
// [
// 'G.E.A.R ACADEMY',
// index: 0,
// input: 'G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY',
// groups: undefined
// ]
//
// [
// 'G.E.A.R ACADEMY',
// index: 33,
// input: 'G.E.A.R ACADEMY is the best we love G.E.A.R ACADEMY',
// groups: undefined
// ]
Esses métodos poderosos podem ajudar você a manipular e extrair dados de strings sem precisar sacrificar desempenho ou legibilidade.Este módulo não possui perguntas. Marque como concluído.