
Очень часто нам нужно отфильтровать массив объектов по нескольким условиям.
У меня есть массив объектов товаров, который мы хотим фильтровать. У каждого товара есть имя, цена, категория и количество.
const products = [
{name: 'Jacket', price: 50, category: 'clothes', count: 20},
{name: 'Boots', price: 120, category: 'clothes', count: 30},
{name: 'Iphone', price: 600, category: 'electronics', count: 5},
{name: 'Ipad', price: 300, category: 'electronics', count: 10}
]
Мы хотим получить массив имен товаров, которые находятся в категории clothes и у которых количество оставшегося товара меньше 50, а также, цена меньше 100.
На чистом js мы могли бы написать так
const getProductNames = items => {
const filteredItems = items.filter(item => item.category === 'clothes' && item.count < 50 && item.price < 100)
return filteredItems.map(item => item.name)
}
console.log(getProductNames(products))
Если мы посмотрим в браузер, то мы получили массив с одним названием. Но этот код достаточно сложно считывать и с Ramda мы можем сделать его лаконичнее.
const getProductNames = R.compose(
R.pluck('name'),
R.filter(item => item.category === 'clothes' && item.count < 50 && item.price < 100)
)
Мы применили compose и pluck и сделали нашу функцию point free. Но метод фильтр по прежнему выглядит ужасно и нечитабельно.
В Ramda, для комбинации нескольких условий, мы можем применять метод where. Он просто создает предикат, который проверяет объект.
Давайте попробуем на нашем примере. Мы можем в filter передать R.where, внутри которого мы укажем наши условия
const getProductNames = R.compose(
R.pluck('name'),
R.filter(R.where({
category: R.equals('clothes')
}))
)
R.where принимает объект, внутри которого мы можем прописывать условия для полей. В данном случаем мы указали, что категория должна равняться clothes.
Если мы посмотрим в браузер, то мы получаем имена, которые находятся в категории clothes.
Теперь мы хотим указать, что количество должно быть меньше 50. Тут есть один нюанс. Функция Ramda lt возвращает true, если первый аргумент меньше второго. Мы не можем написать подобным образом, так как тогда у нас будет неправильный порядок аргументов и мы будет сравнивать, а меньше ли 50 чем текущий count.
const getProductNames = R.compose(
R.pluck('name'),
R.filter(R.where({
category: R.equals('clothes'),
count: R.lt(50)
}))
)
Нам нужно, чтобы первым аргументом был текущий count, а вторым - то, с чем мы сравниваем. В этом нам поможет функция placeholder. Она нужна для работы с каррируемыми функциями, когда мы хотим оставить пропуск для прокидываемого автоматически аргумента.
const getProductNames = R.compose(
R.pluck('name'),
R.filter(R.where({
category: R.equals('clothes'),
count: R.lt(R.__, 50)
}))
)
В этом случае, первым аргументом станет count из текущего продукта, а вторым аргументом - будет 50.
Теперь, давайте добавим фильтр по цене
const getProductNames = R.compose(
R.pluck('name'),
R.filter(R.where({
category: R.equals('clothes'),
count: R.lt(R.__, 50),
price: R.lt(R.__, 100)
}))
)
Если мы посмотрим в браузер, то все работает, как и раньше, но этот код намного читабельнее и его легче поддерживать.
Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.