Продвинутое применение «select» в Ruby

В мире программирования на Ruby универсальный метод select  —  больше, чем просто базовый инструмент для фильтрации массивов.

Многие ограничиваются простыми способами его применения, но имеются и продвинутые, цель которых  —  решение сложных задач, манипулирование данными, удобный для восприятия код.

С пониманием всего потенциала select код Ruby пишется эффективнее и новичками, и профи.

В блоке select с массивом внутри оценивается, истинно условие или ложно, выдается новый, удовлетворяющий условию массив.

Типичное применение метода select  —  выбор нечетных чисел числового массива.

Пример 1:

[1, 2, 3, 4, 5, 6].select { |n| n.odd? }
=> [1, 3, 5]

Методом to_proc сокращаем этот код.

Пример 2:

[1, 2, 3, 4, 5, 6].select(&:odd?)
=> [1, 3, 5]

Продвинутые варианты применения

Метод «select!»

Это один из методов с восклицательным знаком для фильтрации массива на месте: вместо выдачи нового массива здесь меняется исходный.

Пример 3:

first_array = [1, 2, 3, 4, 5, 6]
first_array.select!(&:odd?)
=> [1, 3, 5]
first_array
=> [1, 3, 5]

second_array = %w[ruby python java rust elixir]
second_array.select!{ |programming_language| programming_language.start_with?('r') }
=> ["ruby", "rust"]
second_array
=> ["ruby", "rust"]

«Select» с другими методами

Select комбинируется и объединяется с другими методами Enumerable, таким как with_index, которым для условия select используются значение массива и индекс значения.

Пример 4:

third_array = %w[ruby python java rust elixir]
third_array.select.with_index { |programming_language, index| programming_language.start_with?('r') && index.even? }
=> ["ruby"]

Сложные условия выбора в блоке «select»

А как быть со сложным набором условий? Например, надо выбрать из массива объект, в котором одно из полей не nil и проверить, не старше ли часа временна́я метка updated_at.

Ради простоты примера взяты только два условия. Добавляйте еще, если поместите их всех в одной строке кода: будет, как в примере выше. Если не помещаются или не читаются в одной строке, делайте аналогично примеру ниже.

Пример 5:

# Комбинируется сколько угодно
# условий, как в следующем
# блоке «select».
# [1, 2, 3, 4, 5, 6].select do |n|
# (n.odd? && n == 3) || n.even? || (n % 3).even?
# end


selected_users = User.active.select do |user|
user_full_address = user.get_full_address

# return true/false is not correct
next(false) if user_full_address.blank?
next(user.updated_at < Time.now - 1.hour)
end

Оказывается, next применяется не только для пропуска итерации в цикле, но и для приема значения параметра. Подробнее о next.

В примере выше вместо ключевого слова return нужно next с аргументом true/false. Здесь получается полный адрес пользователя.

В следующей строке проверяется условие: если адрес пуст, с next(false) он не будет выбран.

Вторым условием проверяется, обновлено поле updated_at в последний час или нет. Так получится искомый результат операции select с указанными выше условиями.

Reject  —  метод, противоположный select. К нему применимы те же идеи.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Banura Randika: Advanced uses of ‘select’ in Ruby

Предыдущая статьяШаблоны проектирования в React
Следующая статьяКак узнать, допускает ли изменения коллекция в Java?