Poznajemy kolejny wzorzec, który ja nazywam depth-control. Posiada on warunek bazowy będący maksymalną głębokością, na jaką schodzi. Do dzieła.

Przypomnijmy sobie funkcję flatten:

 const flatten = (nestedArr) => {
    
    const flatArr = [];

    const _flatten = (arr) => {

      let counter = 0

      while (counter < arr.length) {

        const val = arr[counter];

        if (Array.isArray(val)) 
          _flatten(val);
        else 
          flatArr.push(val);
        
        counter++;
      }
    
    }

    _flatten(nestedArr);

    return flatArr;
  }

Ta funkcja będzie spłaszczać aż do skutku, aż nie napotka elementu, który tablicą nie jest albo nie wyczerpie wszystkich elementów.

Ok, zobaczmy jak można rozpłaszczyć bardzo prostą tablicę (1 poziom zagnieżdżenia):

let oneLevelNest = [1,2, [3,4], 5, [6,7]];
let flatOneLevelNest = oneLevelNest.reduce((acc, val) => acc.concat(val), []);
console.log(flatOneLevelNest);
//[ 1, 2, 3, 4, 5, 6, 7 ]

Ok, niech o rekurencyjnym wywołaniu się funkcji decyduje warunek dotyczący typu danych (tablica), lecz o jej zakończeniu decyduje warunek bazowy dotyczący jej głębokości:

function flattenToDepth(arr, depth){

        if(depth === 0)
            return arr.slice();

        return arr.reduce(
            (acc, val) => acc.concat(Array.isArray(val) ? 
            flattenToDepth(val, depth - 1) 
            : val), 
            []);
}

console.log(flattenToDepth(arr, 3));
//[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

Możemy teraz poeksperymentować. Albo napisać bez reduce, w jakiś inny sposób. Koniec końców kontrolowanie na jaką głębokość schodzimy to rzecz często w rekurencji spotykana.