JavaScript .filter () Метод полный анализ

JavaScript

.filter — это встроенный итеративный метод массива, который принимает «предикат (Примечание переводчика: функция условия фильтра)», вызов «предиката» для каждого значения и возвращает условие («правдивое») Массив значений ").

Приведенное выше предложение содержит много информации, давайте ответим на них по порядку.

  • «Встроенный» просто означает, что он является частью языка — вам не нужно добавлять какие-либо библиотеки для доступа к этой функциональности.

  • «Метод итерации» относится к функции, которая принимает функцию для запуска для каждого элемента массива. И .map, и .reduce являются примерами итерационных методов.

  • «Предикат» относится к функциям, принятым в .fifter.

  • «Истинное значение» — это любое значение, которое оценивается как истинное при преобразовании в логическое значение. Почти все значения верны, кроме: undefined, null, false, 0, NaN или "" (пустая строка).

Давайте посмотрим на пример ниже, чтобы увидеть, как работает .filter.

const restaurants = [
    {
        name: "Dan's Hamburgers",
        price: 'Cheap',
        cuisine: 'Burger',
    },
    {
        name: "Austin's Pizza",
        price: 'Cheap',
        cuisine: 'Pizza',
    },
    {
        name: "Via 313",
        price: 'Moderate',
        cuisine: 'Pizza',
    },
    {
        name: "Bufalina",
        price: 'Expensive',
        cuisine: 'Pizza',
    },
    {
        name: "P. Terry's",
        price: 'Cheap',
        cuisine: 'Burger',
    },
    {
        name: "Hopdoddy",
        price: 'Expensive',
        cuisine: 'Burger',
    },
    {
        name: "Whataburger",
        price: 'Moderate',
        cuisine: 'Burger',
    },
    {
        name: "Chuy's",
        cuisine: 'Tex-Mex',
        price: 'Moderate',
    },
    {
        name: "Taquerias Arandina",
        cuisine: 'Tex-Mex',
        price: 'Cheap',
    },
    {
        name: "El Alma",
        cuisine: 'Tex-Mex',
        price: 'Expensive',
    },
    {
        name: "Maudie's",
        cuisine: 'Tex-Mex',
        price: 'Moderate',
    },
];

Это много информации. Я хочу бургер прямо сейчас, поэтому давайте отфильтруем этот массив.

const isBurger = ({cuisine}) => cuisine === 'Burger';
const burgerJoints = restaurants.filter(isBurger);

isBurger — это предикат, а burgerJoints — этоnewМассив, который является подмножеством ресторанов.Стоит отметить, что массив ресторанов не изменился.

Ниже приведен простой пример двух отображаемых списков: необработанный массив ресторанов и отфильтрованный массив burgerJoints.

See the Pen .filter - isBurger by Adam Giese (@AdamGiese) on CodePen.

отрицательный предикат

Для каждого предиката есть противоположный отрицательный предикат.

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

Я ел гамбургеры часами, и теперь я снова голоден. На этот раз я хочу отфильтроватьoutБургеры, чтобы попробовать что-то новое. Один из вариантов — написать новый предикат isNotBurger с нуля.

const isBurger = ({cuisine}) => cuisine === 'Burger';
const isNotBurger = ({cuisine}) => cuisine !== 'Burger';

Однако посмотрите, насколько похожи эти два предиката. это неDRY code. Другой вариант — вызвать предикат isBurger и перевернуть результат.

const isBurger = ({cuisine}) => cuisine === 'Burger';
const isNotBurger = restaurant => !isBurger(restaurant);

Этот лучше! Если определение гамбургера изменится, вам нужно изменить логику только в одном месте. Но что, если нам нужны отрицательные предикаты? Поскольку это то, что мы, вероятно, хотим делать часто, может быть хорошей идеей написать функцию отрицания.

const negate = predicate => function() {
  return !predicate.apply(null, arguments);
}

const isBurger = ({cuisine}) => cuisine === 'Burger';
const isNotBurger = negate(isBurger);

const isPizza = ({cuisine}) => cuisine === 'Pizza';
const isNotPizza = negate(isPizza);

У вас могут возникнуть вопросы.

Что такое .apply?

MDN:

Метод apply() вызывает функцию с заданным this и предоставляет аргументы в виде массива (или объекта, подобного массиву).

Что такое аргументы?

MDN:

Объект arguments — это локальная переменная, доступная во всех (не стрелочных) функциях. Вы можете ссылаться на объект параметра функции внутри функции, используя параметр.

Зачем использовать старый функционал вместо более крутых стрелочных функций?

В этом случае использование традиционных функций необходимо, поскольку объект arguments доступен только для традиционных функций.

К 20 августа 2018 г.Как справедливо отметили некоторые рецензенты, вы можете использоватьостаточный параметриспользоватьСтрелочные функции пишут \negate\.

возвращаемый предикат

Как мы видели на примере функции инвертирования, функции легко вернуть новую функцию в JavaScript. Это полезно для написания «предикатов». Например, давайте рассмотрим наши предикаты isBurger и isPizza.

const isBurger = ({cuisine}) => cuisine === 'Burger';
const isPizza  = ({cuisine}) => cuisine === 'Pizza';

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

const isCuisine = comparison => ({cuisine}) => cuisine === comparison;
const isBurger  = isCuisine('Burger');
const isPizza   = isCuisine('Pizza');

Теперь, что, если мы хотим начать проверять цены?

const isPrice = comparison => ({price}) => price === comparison;
const isCheap = isPrice('Cheap');
const isExpensive = isPrice('Expensive');

Теперь isCheap и isExpensive оба СУХИЕ (Примечание переводчика: не повторяйтесь, принцип программирования, не пишите также повторяющийся код), isPizza и isBurger СУХИЕ, но isPrice и isCuisine могут разделять свою логику!

const isKeyEqualToValue = key => value => object => object[key] === value;

// these can be rewritten
const isCuisine = isKeyEqualToValue('cuisine');
const isPrice = isKeyEqualToValue('price');

// these don't need to change
const isBurger = isCuisine('Burger');
const isPizza = isCuisine('Pizza');
const isCheap = isPrice('Cheap');
const isExpensive = isPrice('Expensive');

Для меня в этом красота стрелочных функций. В одной строке можно элегантно создать функцию третьего порядка.

Видите, как легко создать несколько отфильтрованных списков из исходного массива ресторанов?

See the Pen .filter - returning predicates by Adam Giese (@AdamGiese) on CodePen.

написать предикат

Теперь мы можем отфильтровать наш массив по гамбургеру или дешевой цене... но если вы хотитеcheap burgersКак сделать? Один из вариантов — соединить два фильтра вместе.

const cheapBurgers = restaurants.filter(isCheap).filter(isBurger);

Другой вариант — «объединить» два предиката в один.

const isCheapBurger = restaurant => isCheap(restaurant) && isBurger(restaurant);
const isCheapPizza = restaurant => isCheap(restaurant) && isPizza(restaurant);

Посмотрите на весь дублированный код. Мы определенно могли бы обернуть это в новую функцию!

const both = (predicate1, predicate2) => value =>
  predicate1(value) && predicate2(value);

const isCheapBurger = both(isCheap, isBurger);
const isCheapPizza = both(isCheap, isPizza);

const cheapBurgers = restaurants.filter(isCheapBurger);
const cheapPizza = restaurants.filter(isCheapPizza);

Что делать, если у вас нет пиццы или гамбургеров?

const either = (predicate1, predicate2) => value =>
  predicate1(value) || predicate2(value);

const isDelicious = either(isBurger, isPizza);
const deliciousFood = restaurants.filter(isDelicious);

Это шаг в правильном направлении, но что, если вы хотите включить более двух продуктов? Это не масштабируемый подход. Здесь могут пригодиться два встроенных метода массива. .every и .some являются методами предикатов и также принимают предикаты. .every проверяет, передается ли предикат каждому элементу массива, а .some проверяет, чтоanyПередает ли член предикат.

const isDelicious = restaurant =>
  [isPizza, isBurger, isBbq].some(predicate => predicate(restaurant));

const isCheapAndDelicious = restaurant =>
  [isDelicious, isCheap].every(predicate => predicate(restaurant));

И, как обычно, завернем их в какую-нибудь полезную абстракцию.

const isEvery = predicates => value =>
  predicates.every(predicate => predicate(value));

const isAny = predicates => value =>
  predicates.some(predicate => predicate(value));

const isDelicious = isAny([isBurger, isPizza, isBbq]);
const isCheapAndDelicious = isEvery([isCheap, isDelicious]);

И isEvery, и isAny принимают массив предикатов и возвращают предикат.

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

See the Pen .filter - dynamic filters by Adam Giese (@AdamGiese) on CodePen.

Суммировать

Фильтры — важная часть разработки JavaScript. Независимо от того, выбираете ли вы неверные данные из ответа API или отвечаете на взаимодействие с пользователем, вам понадобится подмножество значений массива бесчисленное количество раз. Я надеюсь, что этот обзор поможет вам манипулировать предикатами, чтобы писать более читаемый и удобный для сопровождения код.