Sᴀᴍ Onᴇᴌᴀ answer has the right idea for small strings, but can be improved by using a Set to hold the vowels rather than an array. This reduces the overhead of Array.includes
which will iterate each character in the vowels array for non matching characters
You can create a set as const vowels = new Set([..."AEIOUaeiou"]);
To encapsulate the constant vowels
use a function to scope vowels
outside the global scope and closure to make it available to the function.
const countVowels = (() => {
const VOWELS = new Set([..."AEIOUaeiou"]);
return function(str) {
var count = 0;
for (const c of str) { count += VOWELS.has(c) }
return count;
}
})();
Or
const countVowels = (() => {
const VOWELS = new Set([..."AEIOUaeiou"]);
return str => [...str].reduce((count, c) => count += VOWELS.has(c), 0);
})();
UNICODE vowels
Of course the first snippet is the better as it is \$O(1)\$ storage and as it uses a Set
(hash table) it is \$O(n)\$ complexity (where \$n\$ is the length of the string) rather than \$O(n*m)\$ (where \$m\$ is the number of vowels). This becomes more important if you are to include the full set of unicode vowels
const countVowels = (() => {
// Reference https://en.wikipedia.org/wiki/Phonetic_symbols_in_Unicode
const VOWELS = new Set([..."AEIOUaeiouiyɨʉiyɯuɪʏeøɘɵɤoɛœɜɞʌɔaɶɑɒʊəɐæɪ̈ʊ̈IYƗɄIYƜUꞮʏEØɘƟɤOƐŒꞫɞɅƆAɶⱭⱰƱƏⱯÆꞮ̈Ʊ̈"]);
return function(str) {
var count = 0;
for (const c of str) { count += VOWELS.has(c) }
return count;
}
})();
NOTE the above snippet does not work.. see below.
Don't split unicode
If you are using Unicode it is important to release that each unicode 16 bit character does not always represent a single visual character
For example the last two vowels "ɪ̈ʊ̈" require two characters to display. eg the string "\u026A\u0308\u028A\u0308" === "ɪ̈ʊ̈"
is true. You can not just count them
as in the expression "ɪ̈ʊ̈".split("").length
will evaluate to 4.
This is even more problematic as \u026A
and \u028A
the first character codes are also vowels "ɪʊ"
To solve for the full set of vowels and keep the complexity at \$O(n)\$ and storage at \$O(1)\$ we can use 3 sets
const countVowels = (() => {
const VOWELS = new Set([ ..."AEIOUaeiouiyɨʉiyɯuʏeøɘɵɤoɛœɜɞʌɔaɶɑɒəɐæIYƗɄIYƜUʏEØɘƟɤOƐŒꞫɞɅƆAɶⱭⱰƏⱯÆ"]);
const VOWELS_DOUBLE = new Set(["ɪ̈", "ʊ̈", "Ɪ̈", "Ʊ̈"]);
const VOWELS_SINGLE = new Set([..."ʊɪƱꞮ"]);
return function(str) {
var count = 0, prev;
for (const c of str) {
count += VOWELS.has(c);
count += VOWELS_DOUBLE.has(prev + c) || VOWELS_SINGLE.has(prev);
prev = c;
}
return count + VOWELS_SINGLE.has(prev);
}
})();