Just to complement the other answers, there are some situations you should take care of, since there are surprises that only the Unicode brings to you :-)
Take the test below:
function comFilter(frase, letraProcurada) {
return [...frase].filter(letra => letra === letraProcurada).length;
}
function comForEach(frase, letraProcurada) {
var total = 0;
[...frase].forEach(letra => {
if (letra === letraProcurada)
total++;
});
return total;
}
function comSplit(frase, letraProcurada) {
return frase.split(letraProcurada).length - 1;
}
function comRegex(frase, letraProcurada) {
var re = new RegExp(letraProcurada, "g");
return [...frase.matchAll(re)].length;
}
function comFor(frase, letraProcurada) {
var quantidade = 0;
for (var i = 0; i < frase.length; i++) {
if (frase[i] == letraProcurada) {
quantidade++
}
}
return quantidade;
}
function testar(frase, letraProcurada) {
console.log(`procurando "${letraProcurada}" em "${frase}"`);
for (const func of [comFilter, comForEach, comSplit, comRegex, comFor]) {
console.log(`- ${func.name} = ${func(frase, letraProcurada)}`);
}
}
testar('até é noé', 'é');
testar('até é noé', 'é');
Basically I took the solutions of the other answers and tested twice each - apparently with the same string, but note that the results are different (in the second test, all counts were zero).
This is because the second string is in NFD form. If you want to know more details about what this is, read here and here, but basically, letters accented as "is" can be represented in two ways: as the character itself "is" or as two characters: the "e" (without accent) and the " (the accent) - the first form is called NFC, and the second, NFD (read the links already indicated to better understand).
But visually you can’t tell the difference, because they’re both rendered the same way. And then there’s trouble when it comes to counting counts, because we loops, in the regex and in split
, the second string - which is in NFD - considers that the "e" and the accent are separate characters, and therefore none of them is equal to the é
, resulting in zero.
To work in these cases, an alternative is to normalize the strings to NFC:
function ocorrencias(frase, letraProcurada) {
var qtd = 0;
letraProcurada = letraProcurada.normalize('NFC');
for (var letra of frase.normalize('NFC')) {
if (letra === letraProcurada)
qtd++;
}
return qtd;
}
And I also use the for...of
, that already iterates by the string characters correctly.
In this way, it even works with letters from other alphabets, and also with emojis:
function comFilter(frase, letraProcurada) {
return [...frase].filter(letra => letra === letraProcurada).length;
}
function comForEach(frase, letraProcurada) {
var total = 0;
[...frase].forEach(letra => {
if (letra === letraProcurada)
total++;
});
return total;
}
function comSplit(frase, letraProcurada) {
return frase.split(letraProcurada).length - 1;
}
function comRegex(frase, letraProcurada) {
var re = new RegExp(letraProcurada, "g");
return [...frase.matchAll(re)].length;
}
function comFor(frase, letraProcurada) {
var quantidade = 0;
for (var i = 0; i < frase.length; i++) {
if (frase[i] == letraProcurada) {
quantidade++
}
}
return quantidade;
}
function ocorrencias(frase, letraProcurada) {
var qtd = 0;
letraProcurada = letraProcurada.normalize('NFC');
for (var letra of frase.normalize('NFC')) {
if (letra === letraProcurada)
qtd++;
}
return qtd;
}
function testar(frase, letraProcurada) {
console.log(`procurando "${letraProcurada}" em "${frase}"`);
for (const func of [comFilter, comForEach, comSplit, comRegex, comFor, ocorrencias]) {
console.log(`- ${func.name} = ${func(frase, letraProcurada)}`);
}
}
testar('até é noé', 'é'); // NFC
testar('até é noé', 'é'); // NFD
testar('abc def xyz', '');
testar('abc def xyz', '');
Note: with emojis will not work if they are a emoji ZWJ Quence, but we are already running too far from the scope of the question...
@Augustovasques If there’s time left I’ll do it later (but I have a hunch: think that my solution will be slower, since you need to normalize the string first).
– hkotsubo
You could also link this other question: https://answall.com/q/443744/69296 :D
– Luiz Felipe
@Augustovasques Tá lá: https://jsbench.me/9xkit4zsbp/1 (the simple was faster, regex was the worst - normalize was the second worst)
– hkotsubo
This link is valuable. Thank you.
– Augusto Vasques