Магия модуля Enumerable в Ruby: #4 Фильтры
января 16, 2011 | Published in Ruby, Основы | 3 Comments
Статьи из цикла Магия модуля Enumerable:
1.Основы
2. Унарный оператор амперсанд “&”
3. Условные операторы
4. Фильтры
5. Создание новых коллекций
6. Методы inject, min и max
Наиболее мощная часть модуля Enumerable — это набор методов, который направлен на помощь вам при фильтрации огромных коллекций для получения более малких коллекции содержащих те элементы, которые вам необходимы.
Метод entries (или to_a)
Не смотря на то, что я пишу об этом методе в статье о методах для фильтрации коллекции,метод entries ничего не фильтрует. Если вы примените этот метод для массива, то вы ничего не заметите. entries возвращает список элементов в коллекции, а для массива будет возвращен собственно сам массив. Вы навеняка сталкивались с этим методом посредствам его псевдонима — метода to_a. Однако, в случае работы с более сложными коллекциями, как, например, наш класс Team, метод entiries будет иметь больший смысл. Ниже представлен класс Team, с которым мы будем работать:
class Team include Enumerable attr_accessor :members def initialize @members = [] end def each &block @members.each{|member| block.call(member)} end end
Давайте поэкспериментируем с методом entries:
team = Team.new team.members = ['joshua', 'gabriel', 'jacob'] team #=> # team.entries #=> ["joshua", "gabriel", "jacob"]
Вы видите, что team является коллекцией, но при этом не является массивом, однако благодаря методу entries мы получаем представление коллекции в виде массива — списка участников. Честно говоря, все это не очень полезно, но хорошо знать, что такой метод существует и знать как он работает.
Метод select (или find_all)
В отличие от метода entries, метод select, возможно, самый используемый фильтр из модуля Enumerable. Он возвращает список всех элементов коллекции, которые проходят проверку, которая в виде блока передается методу select. Пример работы:
team.select{|member| member =~ /^j/} #=> ["joshua", "jacob"]
Здесь мы хотим отфильтровать коллекцию из тех элементов (имен), которые начинаются с буквы «j». Поскольку, метод select ожидает блок кода, вы можете использовать оператор унарный амперсанд, чтобы сделать ваш код более красивым и лаконичным:
team.members[1].freeze #=> "gabriel" team.select(&:frozen?) #=> ["gabriel"]
В примере выше мы «заморозили» второй элемент коллекции — элемент «gabriel». Затем мы вызывали для коллекции метод select с запросом на фильтрацию «замороженных» элементов.
Метод reject
Этот метод противоположен методу select в том, что возвращает элементы коллекции, которые наоборот не проходят проверку в блоке. Давайте напишем код, который возвратит противоположные результаты результату из прошлого примера:
team.reject{|member| member =~ /^j/} #=> ["gabriel"] team.reject(&:frozen?) #=> ["joshua", "jacob"]
Метод partition
Этот маленький метод, вероятно, заслуживает большего признания и более частого использования, чем он имеет их сейчас. Он как одновременный вызов обоих методов select и reject для фильтрации коллекции. Метод prtition возвращает сразу оба списка элементов — список прошедших проверку элементов и список элементов, которые не прошли проверку:
frozen, not_frozen = team.partition(&:frozen?) #=> [["gabriel"], ["joshua", "jacob"]] frozen #=> ["gabriel"] not_frozen #=> ["joshua", "jacob"]
Метод grep
В предыдущих примерах мы использовали регулярные выражения совместно с некоторыми фильтрующими методами. Я не знал, что модуль Enumerable предоставляет специальный метод для тех случаев, которые мы рассматривали выше, пока я не начал работать над этой статьей. Метод grep работает как метод select, с единственным отличием — он принимает лишь шаблон строки. Чтобы понять разницу посмотрите на пример:
#Код: team.select{|member| member =~ /^j/} #=> ["joshua", "jacob"] #Эквивалентен следущему коду: team.grep(/^j/) #=> ["joshua", "jacob"]
Если вы получаете оргазм, когда передаете в методы блоки кода (а кто его не получает???), то вы можете получать его еще чаще, так как в метод grep так же можно передавать блок кода. Если вы передаете блок кода в метод grep, то этот блок кода будет выполнен для каждого элемента коллекции, который соответствует шаблону. Пример:
team.grep(/^j/){|member| member.capitalize} #=> ["Joshua", "Jacob"]
Здесь мы пишем с прописной буквы все элементы коллекции, которые прошли проверку на соответствие регулярному выражению в grep.
Метод detect (так известный как find)
Метод detect работает аналогично методу select с тем лишь отличием, что останавливает работу после нахождения первого элемента коллекции, который соответствует условию. В отличие от остальных методов он не возвращает массив. Вместо массива detect возвращает лишь первый прошедший проверку элемент:
team.detect{|member| member =~ /^j/} #=> "joshua"
По умолчанию detect возвращает значение nil если соответствующих условию элементов и вовсе не найдено, однако вы можете изменить это его поведение, передав в метод detect блок кода, результат выполнения которого будет возвращено вместо стандартного nil:
team.detect(lambda{'turtles'}){|member| member =~ /^t/} #=> "turtles"
Псевдонимом метода detect является метод find, использования которого вы, очевидно, захотите избежать при работе над приложением на Ruby on Rails, так как Active Record имеет свой собственный метод find.
Выводы
Первый шаг к использованию лучшего инструмента для работы — это изучение всех инструментов, которые имеются в вашем распоряжении.
Оригинал статьи на английском языке:
Лучшая благодарность автору блога — ваши комментарии! Задавайте вопросы — вместе будем искать ответы!
января 16, 2011 at 18:10 (#)
Спасибо большое за grep — я как и Вы тоже про него ничего не знал =)
января 16, 2011 at 18:45 (#)
Допишите предложение в entres: «Если вы примените этот метод для массива, то вы ничего не.»
Как-то не окончено звучит..
января 16, 2011 at 19:03 (#)
none, спасибо за замечание. Все дело в том, что я часто тачпад задеваю из-за чего бывает, что части предложения теряются=)