</> Praktum

Главная / JavaScript / Array.filter()

Справочник · JavaScript

Array.filter(): оставляем подходящие элементы

Редакция PraktumОбновлено 6 мин чтенияУровень: новичок

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

Коротко
  • arr.filter((item) => условие) — новый массив из подходящих элементов.
  • Исходный массив остаётся прежним.
  • Результат всегда массив, даже из одного элемента или пустой.
  • Нужен один элемент — find, нужен ответ «есть ли» — some.
  • В функции с фигурными скобками нужен return.

Минимальный пример

const prices = [120, 600, 350, 900];
const expensive = prices.filter((price) => price >= 500);

console.log(expensive); // [600, 900]
console.log(prices);    // [120, 600, 350, 900] — не изменился

Функция получает каждую цену по очереди и возвращает true или false. В результат попадают только цены, для которых вернулось true.

Синтаксис и параметры

const result = array.filter(callback(element, index, array), thisArg);
Параметры filter()
ПараметрЧто это
elementтекущий элемент
indexего индекс, необязательный
arrayсам массив, необязательный
thisArgзначение this внутри обычной функции; со стрелочными функциями не нужен
Возвращаетновый массив; элементы копируются по ссылке

Функция не обязана возвращать именно true: подойдёт любое истинное значение. Непустая строка, число, кроме нуля, объект — истинны; 0, '', null, undefined, NaN — ложны.

Фильтрация массива объектов

const lessons = [
  { title: 'HTML', published: true, minutes: 15 },
  { title: 'CSS', published: false, minutes: 20 },
  { title: 'JavaScript', published: true, minutes: 30 },
];

const visible = lessons.filter((lesson) => lesson.published);
const short = lessons.filter(({ minutes }) => minutes <= 20);
const both = lessons.filter((l) => l.published && l.minutes <= 20);

Это самый частый случай: оставить опубликованные статьи, товары в наличии, активных пользователей. Несколько условий соединяют через && (все сразу) или || (хотя бы одно).

function search(items, query) {
  const q = query.trim().toLowerCase();
  if (!q) return items;
  return items.filter((item) => item.title.toLowerCase().includes(q));
}

search(lessons, 'java'); // [{ title: 'JavaScript', … }]

Обе строки приводятся к одному регистру, иначе «Java» не найдётся по запросу «java». Пустой запрос возвращает весь список — так фильтр не прячет всё, пока человек ничего не ввёл.

Полезные приёмы

// убрать пустые значения: '', 0, null, undefined, NaN, false
const clean = ['HTML', '', null, 'CSS'].filter(Boolean); // ['HTML', 'CSS']

// убрать дубли
const tags = ['css', 'html', 'css'];
const unique = tags.filter((tag, i, arr) => arr.indexOf(tag) === i); // ['css', 'html']
// короче через Set: [...new Set(tags)]

// удалить элемент по id, не меняя исходный массив
const rest = tasks.filter((task) => task.id !== idToRemove);

filter(Boolean) передаёт каждый элемент в функцию Boolean, и остаются только истинные. Удаление через filter — стандартный приём в React и Vue: состояние меняют, создавая новый массив, а не правя старый.

Цепочки filter и map

const titles = lessons
  .filter((lesson) => lesson.published)
  .map((lesson) => lesson.title);
// ['HTML', 'JavaScript']

filter сначала отбирает, map потом преобразует. Порядок важен: если сначала сделать map в строки, поле published уже потеряется.

filter, find, some и findIndex

Какой метод выбрать
НужноМетодВернёт
Все подходящиеfilterмассив, возможно пустой
Первый подходящийfindэлемент или undefined
Индекс первого подходящегоfindIndexчисло или -1
Есть ли хоть одинsometrue / false
Подходят ли всеeverytrue / false

Если нужен один пользователь по id, filter(...)[0] работает, но перебирает весь массив и читается хуже, чем find.

Как filter работает внутри

filter проходит элементы по порядку один раз. Пустые слоты в разреженном массиве (например, после delete arr[1]) пропускаются. Элементы, добавленные в массив во время перебора, не обрабатываются. Результат — поверхностная копия: объекты внутри нового массива те же самые, что в исходном, поэтому изменение visible[0].title изменит и объект в lessons.

filter или цикл for

filter

Коротко, не меняет исходный массив, легко сцепить с map и sort. Подходит в большинстве случаев.

for...of

Нужен, если по ходу надо прервать перебор (break) или сделать сразу несколько действий над элементом.

Частые ошибки

  • Пишут стрелочную функцию с фигурными скобками без return: filter((x) => { x > 5 }) вернёт пустой массив.
  • Ждут от filter один объект и обращаются к result.title — это массив, нужен find или result[0].
  • Передают async-функцию: она возвращает Promise, а он всегда истинный, и в результат попадает всё.
  • Меняют исходный массив внутри callback через splice.
  • Сравнивают строки без приведения к одному регистру в поиске.

Доступность

Если фильтр меняет список на странице, сообщи результат скринридеру: обнови счётчик «Найдено: 3» в элементе с aria-live="polite". Пустой результат показывай текстом «Ничего не найдено», а не просто пустым местом — иначе неясно, сломалось что-то или подходящих элементов нет.

Поддержка браузерами

filter входит в стандарт с ES5 (2009) и работает во всех браузерах и в Node.js. Стрелочные функции, деструктуризация в параметрах и Set из примеров работают во всех актуальных браузерах.

Практика

Дан массив пользователей { name, age, isActive }. Оставь совершеннолетних и активных, затем получи массив только их имён. Отдельно напиши функцию, которая удаляет пользователя по имени и не меняет исходный массив.

Показать решение
const adults = users.filter((user) => user.age >= 18 && user.isActive);
const names = adults.map((user) => user.name);

function removeUser(list, name) {
  return list.filter((user) => user.name !== name);
}

Открыть песочницу

Частые вопросы

Меняет ли filter исходный массив?

Нет. filter всегда возвращает новый массив, а исходный остаётся прежним. Но объекты внутри общие: если изменить поле объекта в результате, оно изменится и в исходном массиве.

Почему filter вернул пустой массив?

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

Как получить один элемент вместо массива?

Используй find: он вернёт первый подходящий элемент или undefined. filter возвращает массив всегда.

Как удалить элемент из массива через filter?

Оставь всё, кроме него: list.filter((item) => item.id !== id). Получится новый массив без этого элемента.

Можно ли использовать async-функцию в filter?

Нет: async-функция возвращает Promise, а он всегда истинный. Сначала дождись проверок через Promise.all, потом фильтруй по полученным результатам.

Источники

Ещё в разделе JavaScript