JavaScript | Как узнать глубину массива?

JavaScript | Как узнать глубину массива?

Функция получения глубины уровня вложенности массивов в массиве

function glubina(arr){
   if(arr.filter(i => i.constructor.name === "Array").length != 0){
      return 1 + glubina([].concat(...arr.filter(i => i.constructor.name === "Array")));
   } else {
      return 0;
   }
}
«Глубина 0» (ноль) означает то, что в массиве нет вложенных массивов. То есть все элементы оригинального массива находятся на верхнем уровне.

 

Та же функция, оформленная тернарным оператором

function glubina(arr){
    return x <= arr.filter(i => i.constructor.name === "Array").length != 0 ? 1 + glubina([].concat(...arr.filter(i => i.constructor.name === "Array"))) : 0;
}

Собственный метод для объектов-прототипов Array

Array.prototype.glubina = function(){
   if(this.filter(i => i.constructor.name === "Array").length != 0){
      return 1 + [].concat(...this.filter(i => i.constructor.name === "Array")).glubina();
   } else {
      return 0;
   }
}

Тот же метод, оформлен тернарным оператором

Array.prototype.glubina = function(){
   return x <= this.filter(i => i.constructor.name === "Array").length != 0 ? 1 + [].concat(...this.filter(i => i.constructor.name === "Array")).glubina() : 0;
}

Как работает функция?

Данная функция вызывает сама себя каждый раз пока не найдёт самый глубокий массив в основном массиве. Каждое «погружение» в глубину основного массива итерируется в возвращаемое значение.

Как только встречается глубокий массив без массивов в качестве элементов, тогда функция возвращает ноль и рекурсивные вызовы прекращаются. К этому моменту уже произошло суммирование итератора и его значение возвращается в числовом виде глубины основного массива.

В основе работы функции используется условие:

.filter(i => i.constructor.name === "Array").length != 0

На первой итерации мы проверяем основной массив на наличие в нём других массивов. Мы обращаемся к каждому элементу основного массива и проверяем его свойство .constructor и .name (имя конструктора). Это свойство возвращает нам строковое представление класса, в котором был создан объект текущей фильтрации.

Условно можно выделить такое деление:

  • фигурные скобки принадлежат классу Object,
  • квадратные скобки принадлежат классу Array,
  • числа принадлежат классу Number
  • строки принадлежат классу String

Если фильтр отберёт хоть что-то, значит в основном массиве есть другие массивы (представители класса Array). Поэтому мы сравниваем длину нового массива.

Когда это условие выполняется, тогда мы возвращаем «1» и прибавляем к этой единице новый вызов функции. В этот новый вызов мы передаём:

[].concat(...arr.filter(i => i.constructor.name === "Array"))

…передаём один новый массив, который получился в результате «склейки» элементов, где были массивы.

С этого момента начинается рекурсия. Вызовы будут продолжаться, пока в массивах будут встречаться другие массивы. Единичка будет приплюсовываться к другой единичке….и так, далее, до победного.

 

Пример работы — Скопируй, Потестируй!

Для тестов возьмём массивы:

var massiv1 = [1, 2, 3, [44, 44, [11,11]], 5, 6, [77, 77], [[ [ [ [] ] ] ]]];
var massiv2 = [1, 2, 3, [44, 44, [11,11]], 5, 6, [77, 77]];
var massiv3 = [1, 2, 3, [44, 44], 5, 6, [77, 77]];
var massiv4 = [1, 2, 3, 5, 6];
var massiv5 = [];
var massiv6 = [,,,];

 

Вызовем каждый функцию по каждому массиву:

glubina(massiv1);
glubina(massiv2);
glubina(massiv3);
glubina(massiv4);
glubina(massiv5);
glubina(massiv6);
Функция получения глубины массива - JavaScript
Функция получения глубины массива — JavaScript

 

Способ № 2 — через строки и квадратные скобки

Можно взглянуть на задачу поиска глубины массива по другому. Например можно говорить о массиве как о строке. То есть на первом шаге этой логики мы преобразуем массив в строку.

Пусть у нас будет массив:

var massiv = [1, [2, 22, [222, [ 2222]]], 3, 4, 5, 6, [66, [666,,,,,[6666]]], 7, 8]

Мы воспользуемся встроенным конструктором JSON и его методом stringify()

var s1 = JSON.stringify(massiv)

В результате конвертации мы получим такую строку:

"[1,[2,22,[222,[2222]]],3,4,5,6,[66,[666,null,null,null,null,[6666]]],7,8]"

Если подойти к решению задачи объективно, то нам вообще всё равно какие значения находятся внутри массива. То есть из этой строки мы можем безболезненно выбросить все значения и оставить только квадратные скобки. Нам даже запятые не нужны.

Сперва раскидываем каждый символ на элемент массива:

var m1 = [...s1]

Теперь новый массив выглядит так:

["[", "1", ",", "[", "2", ",", "2", "2", ",", "[", "2", "2", "2", ",", "[", "2", "2", "2", "2", "]", "]", "]", ",", "3", ",", "4", ",", "5", ",", "6", ",", "[", "6", "6", ",", "[", "6", "6", "6", ",", "n", "u", "l", "l", ",", "n", "u", "l", "l", ",", "n", "u", "l", "l", ",", "n", "u", "l", "l", ",", "[", "6", "6", "6", "6", "]", "]", "]", ",", "7", ",", "8", "]"]

Фильтруем этот массив по наличию левой и правой квадратной скобки. Напоминаю, что они в виде строки лежат в массиве:

var m2 = m1.filter(i=>(i=="[" || i=="]"))

Получаем отфильтрованный массив:

[ "[", "[", "[", "[", "]", "]", "]", "[", "[", "[", "]", "]", "]", "]" ]

Теперь собираем символы скобок в одну строку:

var s2 = m2.join("")

Получили такую строку:

"[[[[]]][[[]]]]"

Что мы видим? Теперь нашим ориентиром будет являться последовательное сочетание левой и правой скобки — []

 

Если мы будем удалять такие сочетания, то количество «удалений» будет равно глубине массива. То есть мы удаляем пары скобок и смотрим на строку заново. Если есть пары, то снова удаляем. А в это время у нас будет счётчик прибавляться на 1 каждый раз.

Сделаем это через цикл for и регулярные выражения:

counter=-1;
for(s="[[[[]]][[[]]]]"; s.length != 0; counter++){
   s = s.replace(/\[\]/g, "");
}

Почему мы взяли счётчик со стартовым значением -1? Дело в том, что JSON преобразовал наш массив с «внешними границами» в виде левой и правой квадратными скобками. В строке они будут лишними и поэтому счётчик «как бы не будет учитывать последнее извлечение».

Давайте по шагам посмотрим о чём идёт речь:

s="[[[[]]][[[]]]]"

s = s.replace(/\[\]/g, "")
"[[[]][[]]]"

s = s.replace(/\[\]/g, "")
"[[][]]"

s = s.replace(/\[\]/g, "")
"[]"

s = s.replace(/\[\]/g, "")
""
Строка опустела за 4 шага - JavaScript
Строка опустела за 4 шага — JavaScript

 

Строка «опустошилась» за 4 (четыре) вызова замены. Но если мы посмотрим на исходный массив, то в нём максимальная глубина вложенности равна 3 (трём).

Глубины элементов оригинального массива - JavaScript
Глубины элементов оригинального массива — JavaScript

Поэтому счётчик стартует с -1.

 

Функция нахождения глубины массива через строки и квадратные скобки

function glubinaMassiva (arr){
   counter=-1;
   for(s=[...JSON.stringify(arr)].filter(i=>(i=="[" || i=="]")).join(""); s.length != 0; counter++){
      s = s.replace(/\[\]/g, "");
   }
   return counter;
}

или

function glubinaMassiva (arr){ 
   for(counter=-1, s=[...JSON.stringify(arr)].filter(i=>(i=="[" || i=="]")).join(""); s.length != 0; counter++){
      s = s.replace(/\[\]/g, ""); 
   } 
   return counter; 
}

Скрин из VSC

Функция нахождения глубины массива через строки и квадратные скобки - JavaScript
Функция нахождения глубины массива через строки и квадратные скобки — JavaScript

Пример для теста:

glubinaMassiva([[[[[[[[[]]]]]]]],[[[[[[[]]]]]]]])
8

Информационные ссылки

JavaScript | Как узнать глубину каждого элемента массива?

JavaScript | Как вытащить элементы массивов в массивах на один уровень?

JavaScript | Array | constructor

JavaScript | Как проверить наличие массива в массиве?