I need to count vowels in nested array, and I'd like to do it with closure to avoid global namespace pollution. Here's my code:
let nestedArr = [
"Elie",
["Matt", ["Tim"]],
["Colt", ["Whiskey", ["Janey"], "Tom"]],
"Lorien"
];
function countVowels() {
let vowelsCount = 0;
let vowels = ['a', 'e', 'i', 'o', 'u'];
return function foo(arr) {
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === 'string') {
for (let letter of arr[i]) {
if (vowels.includes(letter.toLowerCase())) {
vowelsCount++;
}
}
} else {
return foo(arr[i]);
}
}
return vowelsCount;
}
}
const counter = countVowels();
console.log(counter(nestedArr));
I expect correct number of vowels, but get 5. I tried to debug and see it just stops after "Tim" which is the deepest subarray, so obviously my function does not go level up and I am missing something.
How can I achieve this?
Thank you in advance.
You don't need the nested function, you can declare a single recursive function and still keep everything self-contained.
const countVowels = (arr) => {
const vowels = ['a', 'e', 'i', 'o', 'u'];
const vowel_count = (s) => [...s].filter((c) => vowels.includes(c.toLowerCase())).length;
let vowels_total = 0;
for (const e of arr) {
vowels_total += Array.isArray(e) ? countVowels(e) : vowel_count(e);
}
return vowels_total;
};
const nestedArr = ['Elie', ['Matt', ['Tim']], ['Colt', ['Whiskey', ['Janey'], 'Tom']], 'Lorien'];
console.log(countVowels(nestedArr));
Or, foregoing recursion with a curried closure using Array#flat() (to Infinity)
const countVowels = (
(v) => (arr) =>
[...arr.flat(Infinity).join('')].filter((c) => v.includes(c.toLowerCase())).length
)(['a', 'e', 'i', 'o', 'u']);
const nestedArr = ['Elie', ['Matt', ['Tim']], ['Colt', ['Whiskey', ['Janey'], 'Tom']], 'Lorien'];
console.log(countVowels(nestedArr));
Your function works just fine if you just change
return foo(arr[i]);
To:
foo(arr[i]);
You need to let the entire loop run (obviously), the return makes it stop earlier.
let nestedArr = [
"Elie",
["Matt", ["Tim"]],
["Colt", ["Whiskey", ["Janey"], "Tom"]],
"Lorien"
];
function countVowels() {
let vowelsCount = 0;
let vowels = ['a', 'e', 'i', 'o', 'u'];
return function foo(arr) {
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === 'string') {
for (let letter of arr[i]) {
if (vowels.includes(letter.toLowerCase())) {
vowelsCount++;
}
}
} else {
foo(arr[i]);
}
}
return vowelsCount;
}
}
const counter = countVowels();
console.log(counter(nestedArr));
This mostly sounds like an exercise to learn recursion. If so, I leave you to the other fine recursive answers here.
But a simple alternative is to note the toString format for Arrays, and realize that we can simply apply our tests to the result of that, leaving some very simple code:
const countVowels = (a) =>
[... a .toString () .toLowerCase ()] .filter (c => 'aeiou' .includes (c)) .length
const nestedArr = ['Elie', ['Matt', ['Tim']], ['Colt', ['Whiskey', ['Janey'], 'Tom']], 'Lorien']
console .log (countVowels (nestedArr))