Home > Software engineering >  Is it possible to use closure and recursive approach for nested arrays in Javascript
Is it possible to use closure and recursive approach for nested arrays in Javascript

Time:10-16

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>

  • Related