.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?
Метод apply() вызывает функцию с заданным this и предоставляет аргументы в виде массива (или объекта, подобного массиву).
Что такое аргументы?
Объект 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 или отвечаете на взаимодействие с пользователем, вам понадобится подмножество значений массива бесчисленное количество раз. Я надеюсь, что этот обзор поможет вам манипулировать предикатами, чтобы писать более читаемый и удобный для сопровождения код.