Четыре метода поиска в массивах JavaScript

В JavaScript имеется множество полезных способов поиска элементов в массивах. Вы всегда можете использовать простой цикл for, но в ES6+ есть много методов циклического перебора массива, позволяющих легко найти все необходимое.

Какие же из множества разных методов использовать в каждом случае? Например, нужно ли вам знать при поиске, есть ли вообще в массиве такой элемент? Хотите ли вы получить индекс элемента или сам элемент?

Мы расскажем о нескольких разных методах, но важно понимать, что все эти методы являются встроенными методами прототипа Array. Это означает, что вам просто нужно прикрепить их к любому массиву, используя точечную нотацию. Также это означает, что данные методы недоступны для объектов или чего-либо еще, кроме массивов (хотя есть пересечение со строками).

Мы рассмотрим следующие методы массивов:

  • Array.includes
  • Array.find
  • Array.indexOf
  • Array.filter

includes

const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];  alligator.includes("thick scales"); // returns true 

Метод .includes() возвращает логическое значение и позволяет определить, есть ли в массиве указанный элемент. Он выдает простой ответ true или false. Базовый синтаксис выглядит так:

arr.includes(valueToFind, [fromIndex]); 

Как видите, в нашем примере у нас был только один параметр — valueToFind. Это значение, совпадение с которым нужно было найти в массиве. Дополнительный параметр fromIndex — это число, показывающее, от какого индекса нужно начинать поиск (значение по умолчанию 0, так что поиск выполняется во всем массиве). Итак, поскольку в нашем примере элемент ‘thick scales’ находится на индексе 0, следующий код выдаст значение false: alligator.includes('thick scales', 1); потому что он выполняет поиск, начиная с индекса 1 и далее.

Теперь нужно отметить несколько важных вещей. Этот метод .includes() использует строгое сравнение. Если взять предыдущий пример, это означает, что следующий код выдаст значение false: alligator.includes('80'); Это связано с тем, что хотя 80 == '80' имеет значение true, 80 === '80' имеет значение false — разные типы не проходят строгое сравнение.

find

Чем метод .find() отличается от метода includes()? Если в нашем примере мы просто заменим “includes” на “find”, мы получим следующее сообщение об ошибке:

Uncaught TypeError: thick scales is not a function 

Это связано с тем, что метод find требует передачи функции. Метод find не будет просто использовать простой оператор сравнения как это делает “includes()”. Вместо этого он будет передавать каждый элемент в функцию и смотреть, какой результат она возвращает: true или false. Итак, хотя это сработает: alligator.find(() => 'thick scales');, вы наверное захотите поместить в функцию собственный оператор сравнения, чтобы она возвращала актуальный результат.

const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];  alligator.find(el => el.length < 12); // returns '4 foot tail' 

Эта простая функция в нашем методе find ищет каждый элемент массива с псевдонимом 'el’, который мы присвоили, и останавливается, когда находит первый результат, дающий значение true. В нашем случае true имеет свойство длины менее 12 (у чисел нет свойства длины). Разумеется, вы можете сделать эту функцию настолько сложной, насколько это необходимо, чтобы условие true соответствовало вашим потребностям.

Обратите внимание, что этот код не возвращает true. Метод find возвращает не логическое значение, а первый совпадающий элемент. Если совпадающего элемента нет, то есть, если ни один элемент не соответствует критериям, определенным в вашей функции, она возвращает значение undefined. Обратите внимание, что функция возвращает только первое совпадение, так что если критериям соответствует несколько элементов, функция выведет только первый из них. Если бы в нашем примере была другая строка длиной меньше 12 после '4 feet tall’, результат бы не изменился.

В нашем примере мы использовали вызов с одним параметром. Вы также можете добавить параметры для ссылки на индекс текущего элемента. Другим параметром может быть весь массив целиком, но этот параметр используют редко. Приведем пример с использованием индекса:

alligator.find((el, idx) => typeof el === "string" && idx === 2); // returns '4 foot tall' 

Мы знаем, что в нашем массиве есть 3 разных элемента, соответствующих первому условию (typeof el === 'string’). Если бы это было единственное условие, метод бы возвратил первый результат, 'thick scales’. Однако отличие заключается в том, что только у одного элемента есть индекс 2, и это элемент '4 foot tall’.

Если говорить об индексах, метод .findIndex() очень похож на этот. Этот метод также принимает функцию, но, как вы можете догадаться, он возвращает индекс совпадающего элемента, а не сам совпадающий элемент.

indexOf

const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];  alligator.indexOf("rounded snout"); // returns 3 

Как и метод .includes(), метод .indexOf() использует строгое сравнение, а не функцию как метод .find(). Однако, в отличие от includes(), он возвращает индекс элемента, а не логическое значение. Вы также можете указать, с какого индекса массива можно начать поиск.

Я считаю indexOf() очень полезным методом. Это быстрый и удобный метод, позволяющий узнать, существует ли элемент в массиве, и где именно в массиве он находится. Как он показывает, существует ли элемент? Если он возвращает положительное число, мы знаем, что элемент существует, а если он возвращает значение -1, мы понимаем, что такого элемента нет.

alligator.indexOf("soft and fluffy"); // returns -1 alligator.indexOf(80); // returns 1 alligator.indexOf(80, 2); // returns -1 

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

Как и другие методы, indexOf() возвращает индекс первого найденного совпадающего элемента. JavaScript позволяет использовать альтернативный метод массива, .lastIndexOf(). Как вы можете догадаться, он делает то же, что и метод indexOf(), но начинает с последнего индекса массива и движется назад. Также вы можете указать второй параметр, но помните, что индексы не меняются просто потому, что вы используете другой метод.

const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];  alligator.indexOf(80); // returns 1 alligator.lastIndexOf(80); // returns 4 alligator.indexOf(80, 2); // returns 4 alligator.lastIndexOf(80, 4); // returns 4 alligator.lastIndexOf(80, 3); // returns 1 

Бонус: filter

const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];  alligator.filter(el => el === 80); //returns [80, 80] 

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

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

Заключение

Самый удобный для меня метод поиска — это метод find(), но, как видите, все зависит от конкретной ситуации.

  • Нужно ли вам просто узнать, есть ли такой элемент? Используйте .includes().
  • Вы хотите получить сам элемент? Используйте .find() или .filter(), если вам нужно несколько элементов.
  • Хотите найти индекс элемента? Используйте .indexOf() или findIndex() для более сложного поиска.

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

const jungle = [   { name: "frog", threat: 0 },   { name: "monkey", threat: 5 },   { name: "gorilla", threat: 8 },   { name: "lion", threat: 10 } ];  // break the object down in order to use .includes() or .indexOf() const names = jungle.map(el => el.name); // returns ['frog', 'monkey', 'gorilla', 'lion'] console.log(names.includes("gorilla")); // returns true console.log(names.indexOf("lion")); // returns 3 - which corresponds correctly assuming no sorting was done  // methods we can do on the array of objects console.log(jungle.find(el => el.threat == 5)); // returns object - {name: "monkey", threat: 5} console.log(jungle.filter(el => el.threat > 5)); // returns array - [{name: "gorilla", threat: 8}, {name: 'lion', threat: 10}]