#8 Фильтрация массива с помощью Ramda where

poster
Очень часто нам нужно отфильтровать массив объектов по нескольким условиям. В этом нам поможет Ramda where.
Понравилось? Поделитесь с друзьями!
Понравилось?
Поделитесь с друзьями!
Комментарии
Текст видео

Очень часто нам нужно отфильтровать массив объектов по нескольким условиям.

У меня есть массив объектов товаров, который мы хотим фильтровать. У каждого товара есть имя, цена, категория и количество.

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)
  }))
)

Если мы посмотрим в браузер, то все работает, как и раньше, но этот код намного читабельнее и его легче поддерживать.

Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.

Только зарегистрированные пользователи могут оставлять комментарии.  Войдите, пожалуйста.
Matvey
12 месяцев назад
Добрый день! Спасибо за интересный материал! Но требуется Ваша помощь для большего понимания, так как это первое знакомство с Ramda. Не могу сообразить, как сравнить значения всех ключей всех объектов в массиве с имеющейся строкой и получить в итоге массив объектов в которых были совпадения. Подскажите, пожалуйста, в сторону каких методов смотреть.
Matvey
12 месяцев назад
Все, понял. Точно также нужно использовать filter.
monsterlessons
12 месяцев назад
Вы абсолютно правы. Например, так: R.filter(R.propEq('name', 'aaa'), [{name: 'aaa'}])
pixel
1год назад назад
Здравствуйте. Equals ведь сравнивает два аргумента между собой, а какое значение он имеет внутри where.
pixel
1год назад назад
Все, разобрался.
monsterlessons
1год назад назад
Добрый день. Хорошо, что разобрались. Для лучшего понимания каждой функции ramda, мне помогает их исходный код и тесты. Они очень хорошо помогают увидеть различные кейсы. https://github.com/ramda/ramda/blob/master/src/where.js https://github.com/ramda/ramda/blob/master/test/where.js
Maksim Kostromin
1год назад назад
вместо уродливого логического условия: array .filter(expr1 && expr2 && expr3) просто пишите пайплайн условий: array .filter(expr1) .filter(expr2) .filter(expr3)
monsterlessons
1год назад назад
Как вариант
Ренат Рысаев
7 месяцев назад
И получится что мы будем перебирать массив три раза, так делать нельзя