Ruby Enumerable Magic: Filters

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates

A powerful part of the Enumerable module is the set of methods that help you filter a larger collection down to a smaller one, containing just the items you need. There are several methods to help.

entries (or to_a)

This is the most permissive of the Enumerable filter methods – it doesn’t filter out anything. In fact, if you use this on an array you won’t see any difference at all. entries returns the list of items in the collection. For an array, the array itself will be returned. This makes sense, when you see that this method is aliased as to_a.

But more complex collections like our Team class will provide a better example. Here’s the class, in case you missed it from previous posts:

class Team
  include Enumerable
  attr_accessor :members
  def initialize
    @members = []
  def each &block

Let’s use entries:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team =
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team
=> #<Team:0x100391088 @members=["joshua", "gabriel", "jacob"]>
irb(main):005:0> team.entries
=> ["joshua", "gabriel", "jacob"]

You can see that team is a non-array collection, but calling entries returns the members list. To be honest, this isn’t immensely useful – but it’s good to know that it exists, and how it works.

select (or find_all)

In contrast to entries, select is arguably the most used enumerable filter. It returns a list of all the collection items that pass the test that you pass in as a block. For example:

irb(main):006:0>{|member| member =~ /^j/}
=> ["joshua", "jacob"]

Here we want to filter the collection to just those items that start with the letter “j”. As with all Ruby methods that expect a block, we can use the unary ampersand operator to make our code shorter and sweeter:

irb(main):007:0> team.members[1].freeze
=> "gabriel"
=> ["gabriel"]

In the example above, we “froze” the second member in the collection, “gabriel”. Then we called select on the collection, asking for just the frozen members.


This is the opposite of select. It will only return items in the collection that *don’t* pass the test we give it. Let’s do the opposite of our last two tests:

irb(main):009:0> team.reject{|member| member =~ /^j/}
=> ["gabriel"]
irb(main):010:0> team.reject(&:frozen?)
=> ["joshua", "jacob"]


This little method probably deserves a lot more use and recognition than it gets. You might say it’s the Bruce Campbell of enumerable methods. It’s like calling select AND reject on a collection. it gives you both lists back – the passers, and the failures:

irb(main):011:0> frozen, not_frozen = team.partition(&:frozen?)
=> [["gabriel"], ["joshua", "jacob"]]
irb(main):012:0> frozen
=> ["gabriel"]
irb(main):013:0> not_frozen
=> ["joshua", "jacob"]


In our previous examples, we’ve used regular expressions in some of our filter methods. To be honest, I do this a lot. I didn’t realize that Enumerable provided a special method just for this use case until I researched this article. It works like select, except it only takes a regular expression pattern, no need to pass it a block. That means this snippet:

irb(main):014:0>{|member| member =~ /^j/}
=> ["joshua", "jacob"]

Can be rewritten like this:

irb(main):015:0> team.grep(/^j/)
=> ["joshua", "jacob"]

If you like passing blocks to methods (and who doesn’t??) then you’re in luck, because you can still do that with grep. If you pass a block, each collection item that matches the regular expression will be run through the block, and the results will be gathered up into the return array for you. Like this:

irb(main):016:0> team.grep(/^j/){|member| member.capitalize}
=> ["Joshua", "Jacob"]

grep found our matches, passed each one through capitalize, and gave us a list of the results.

detect (or find)

detect works a lot like select, except it stops looking as soon as it finds the first match. Unlike the other methods, it doesn’t return an array. It returns just the single item:

irb(main):017:0> team.detect{|member| member =~ /^j/}
=> "joshua"

By default, detect will return nil if no match is found. But you can pass a block, which it will run and return, if no match is found:

irb(main):018:0> team.detect(lambda{'turtles'}){|member| member =~ /^t/}
=> "turtles"

An alias for this method is find, which you’ll obviously want to avoid if you’re in a Rails application, since ActiveRecord has its own find method, and you don’t want to make your code harder to read.


Don’t be a fool, like I was, and wait until you write your own article about enumerables to brush up on all the filter methods. I remember the first time I changed the brakes on my own car. One nut would not come loose, and I was even using vise grips on it. After a few hours, I called a friend who brought over a tool that actually tightens the more you crank it (unlike vise grips where you determine the pressure beforehand) and it was off in under a minute. The first step in knowing the right tool for the job is knowing what tools you have at your disposal.


Tags: , , , , , , , , ,

6 Responses to “Ruby Enumerable Magic: Filters”

  1. Ruby Enumerable Magic: New Collections « Kansas City on Rails Says:

    […] Filters […]

  2. Ruby Enumerable Magic: Aggregates « Kansas City on Rails Says:

    […] Filters […]

  3. Ruby Enumerable Magic: Unary Ampersand Operator « Kansas City on Rails Says:

    […] Filters […]

  4. Ruby Enumerable Magic: Booleans « Kansas City on Rails Says:

    […] A Freelance Ruby on Rails Developer in Kansas City « Routing in Ruby on Rails 3 Ruby Enumerable Magic: Filters […]

  5. Ruby Enumerable Magic: The Basics « Kansas City on Rails Says:

    […] Filters […]

  6. Enumerable Фильтры | Разработка на Ruby и Rails c нуля Says:

    […] Оригинал статьи на английском языке: Ruby Enumerable Magic: Filters […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: