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.
CodePudding user response:
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));
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
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));
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
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));
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
let nestedArr = [
'Elie',
['Matt', ['Tim']],
['Colt', ['Whiskey', ['Janey'], 'Tom']],
'Lorien',
];
function countVowels(str) {
return str.match(/['a', 'e', 'i', 'o', 'u']/gi)?.length || 0;
}
function vowelsAmount(arr) {
return arr.reduce((accumulator, currentElement) => {
return Array.isArray(currentElement)
? (accumulator = vowelsAmount(currentElement))
: (accumulator = countVowels(currentElement));
}, 0);
}
console.log(vowelsAmount(nestedArr));
<iframe name="sif4" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>