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

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

Эту задачу можно понять несколькими смыслами:

  1. Мы в принципе хотим понять существует ли массив с квадратными скобками в основном массиве
  2. Мы хотим понять, есть ли похожий массив в основном массиве (с теми же элементами и их значениями)

В этой публикации мы рассмотрим эти два варианта трактования вопроса. Начнём с простого.

 

Есть ли какой-то массив в основном массиве? Он существует в виде значения элемента основного массива?

Есть основной массив с одним объектом, одним массивом, числами и строками:

var massiv = [1, 2, 3, {a:"a"}, [777], 4, 5, "efim360.ru", "javascript"]

Мы будем проверять существование какого-либо массива в основном массиве при помощи метода filter(). Внутрь метода filter() мы будем передавать анонимную функцию, которая будет возвращать нам true или false по заданному условию. Если будет true, то элемент попадёт в новый массив (отфильтрованный), если false, то не попадёт.

Т. к. мы имеем дело с прототипами классов, то условие сравнения будет определяться при помощи «имени конструктора«, в котором этот элемент был создан. В качестве цели будет выбран класс Array, а его имя конструктора будет строкой «Array«. Не перепутайте, а то не получится. Мы сравниваем строки!

massiv.filter(i => i.constructor.name == "Array")
Метод filter нашёл массив в массиве - JavaScript
Метод filter нашёл массив в массиве — JavaScript

Мы вызвали filter() и в ответ получили новый массив (отфильтрованный) с одним элементом. Это как раз то, что мы искали. Мы отловили в основном массиве какой-то массив.

Мы имеем новый массив (отфильтрованный) длиной 1. Если бы в нашем основном массиве небыло бы массивов, тогда фильтр вернул бы нам массив нулевой длины. Вот это и будет нашим условием существования какого либо объекта в массиве:

(massiv.filter(i => i.constructor.name == "Array").length > 0)
Условие существования массива в массиве - JavaScript
Условие существования массива в массиве — JavaScript

Получили ИСТИНУ (TRUE) значит в основном массиве есть массив.

Справка

Мы всегда можем распознать объект по его принадлежности к классу. В нашем случае:

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

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

massiv.map(i=>[i, i.constructor.name])
Получили имена конструкторов значений элементов массива и сопоставили - JavaScript
Получили имена конструкторов значений элементов массива и сопоставили — JavaScript

 

Есть ли похожий массив в массиве?

На эту тему можно спорить долго. «Одинаковость» массивов я буду определять по Наборам элементов. Если наборы совпадают — значит элементы равны (очень похожи друг на друга). Вы можете придумать свой вариант схожести массивов.

Есть массив:

var massiv = [[1,2,3], [3,2,1], [1,2,3,4], 4, 5, 6, "efim360.ru", "javascript", true]

Именно в таком виде, где порядок значений внутренних массивов отличается.

Предположим, что мы хотим узнать существование массива [3,2,1] в основном массиве. Есть ли похожий? Как это сделать?

Давайте с «подводных камней». Как вам такое условие?

Неравенство идентичных массивов - JavaScript
Неравенство идентичных массивов — JavaScript

Для кого-то это будет шок! Это значит, что мы не можем сравнивать массивы «в тупую» как они есть. Что делать?

Нужно привести массивы к строке и сравнивать строки. Поможет нам в этом конструктор JSON и его метод stringify(). В чём его слабость? В том, что он НЕ сортирует ключи перед упаковкой в строку. 🙂

Смотрим как это работает:

JSON.stringify([3,2,1])
"[3,2,1]"

JSON.stringify([1,2,3])
"[1,2,3]"

JSON.stringify([3,2,1]) == JSON.stringify([1,2,3])
false
JSON.stringify не сортирует элементы массивов - JavaScript
JSON.stringify не сортирует элементы массивов — JavaScript

Итоговые строки не равны — false

Сейчас нам нужна функция, которая умеет сортировать значения в массивах, до их передачи в stringify()

Мы можем сделать так:

  1. Создаём новый набор Set
  2. Конвертируем набор в массив
  3. Сортируем массив
  4. Переводим массив в строку
  5. Сравниваем строки

 

Поехали

JSON.stringify(Array.from(new Set([3,2,1])).sort())
"[1,2,3]"

JSON.stringify(Array.from(new Set([1,2,3])).sort())
"[1,2,3]"

JSON.stringify(Array.from(new Set([3,2,1])).sort()) == JSON.stringify(Array.from(new Set([1,2,3])).sort())
true

 

Сравнили два массива из набора - получили true - JavaScript
Сравнили два массива из набора — получили true — JavaScript

Мы научились сравнивать массивы и теперь можем вернуться к нашему основному массиву и проверить есть ли в нём такой массив или нет.

Будем использовать тот же самый filter()

massiv.filter(i=>JSON.stringify(Array.from(new Set(i)).sort()) == JSON.stringify(Array.from(new Set([3,2,1])).sort())  )

В таком виде команда может не сработать, т. к. попытка приведения неитерируемого объекта к Набору вызовет ошибку:

Ошибка приведения к Набору - JavaScript
Ошибка приведения к Набору — JavaScript

new Set(4) — вызовет ошибку. По этой причине нам нужна двойная фильтрация!

 

Итог

Первая фильтрация:

massiv.filter(i => i.constructor.name == "Array")

 

Отловили все массивы в основном массиве

Первая фильтрация для отлова массивов - JavaScript
Первая фильтрация для отлова массивов — JavaScript

 

Вторая фильтрация:

massiv.filter( i=>i.constructor.name == "Array" ).filter( i => JSON.stringify(Array.from(new Set(i)).sort()) == JSON.stringify(Array.from(new Set([3,2,1])).sort())  )
Вторая фильтрация для приведения массивов к строкам - JavaScript
Вторая фильтрация для приведения массивов к строкам — JavaScript

Отыскали похожий массив на наш. Даже два похожих. Хочу обратить ваше внимание, что массив [1,2,3,4] был исключён из фильтрации по причине наличия в нём значения «4».

Длина отфильтрованного массива больше «0» (нуля), значит искомый массив встречается в основном массиве. Это и есть условие:

massiv.filter( i=>i.constructor.name == "Array" ).filter( i => JSON.stringify(Array.from(new Set(i)).sort()) == JSON.stringify(Array.from(new Set([3, 2, 1])).sort())  ).length > 0
true

 

Задача выполнена!

 

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

JavaScript | Равенство объектов

JavaScript | Как узнать экземпляром какого класса является объект?

Стандарт ECMAScript — Раздел «25.5 The JSON Object» — https://tc39.es/ecma262/#sec-json-object