I haven’t posted here in a very long time, but I recently got a full-time job using Ruby and Rails (hurray) so Ruby is more on my mind lately. In fact I’ve gotten a better understanding of what life is like for the average Rails developer by seeing how my co-worker Alex writes his Ruby code. Now Alex is a smart guy, he has been doing web-sites for years, is proficient in ColdFusion, PHP , Flash, HTML and CSS, yet his Ruby code is not always as elegant as he or I would like. Of course I’ve been using Ruby for almost 6 years so know it quite well (and that is a big reason why I was hired.)
Still even I find the occasional new nugget and figured this blog would be a good forum to expose some of my new insights. This way other Rails developers like Alex who aren’t as proficient in Ruby as they would like can benefit from my experience.
Recently I was perusing the documentation for the Enumerable module and took a closer look at the grep method. This method is surprisingly more powerful than it might seem at first glance. To learn more, please continue reading this entry…
What the Documentation Says
First, let’s describe the basic workings of the grep method, straight from the Ruby documentation:
enumObj.grep( pattern ) -> anArray
enumObj.grep( pattern ) {| obj | block } -> anArray
Returns an array of every element in enumObj for which Pattern === element. If the
optional block is supplied, each matching element is passed to it, and the
block's result is stored in the output array.
The most obvious use of grep is with arrays of Strings and a RegExp as the argument:
irb(main):001:0> names = ["Joe", "Bill", "Jill", "Susan", "Sam"]
=> ["Joe", "Bill", "Jill", "Susan", "Sam"]
irb(main):002:0> names.grep(/^J/)
=> ["Joe", "Jill"]
irb(main):003:0> names.grep(/^S/) {|name| name.upcase}
=> ["SUSAN", "SAM"]
Getting Deeper
But the key thing to remember is that grep actually uses the === operator when comparing the argument passed to each element in the Array. So any class that intelligently implements that operator can be used:
irb(main):004:0> numbers = [1, 2, 3, 4, 5, 6, 8, 9]
=> [1, 2, 3, 4, 5, 6, 8, 9]
irb(main):005:0> numbers.grep(3..6)
=> [3, 4, 5, 6]
irb(main):006:0> dates = [Date.new(2000, 1, 1), Date.new(2002, 2, 2),
Date.new(2004, 3, 3), Date.new(2006, 4, 4)]
=> [#<Date: 4903089/2,0,2299161>, #<Date: 4904615/2,0,2299161>,
#<Date: 4906135/2,0,2299161>, #<Date: 4907659/2,0,2299161>]
irb(main):007:0> dates.grep(Date.new(2001, 1, 1)..Date.new(2005, 1, 1)) {|date| date.to_s }
=> ["2002-02-02", "2004-03-03"]
irb(main):008:0> class Base;end
=> nil
irb(main):009:0> class Child1 < Base;end
=> nil
irb(main):010:0> class Child2 < Base;end
=> nil
irb(main):011:0> class NotAChild;end
=> nil
irb(main):012:0> objects = [Child1.new, NotAChild.new, Child2.new]
=> [#<Child1:0x2df6ce8>, #<NotAChild:0x2df6cd4>, #<Child2:0x2df6cc0>]
irb(main):013:0> objects.grep(Base)
=> [#<Child1:0x2df6ce8>, #<Child2:0x2df6cc0>]
In the above examples the Range#=== and Class#=== operators to grep through numbers, dates and instances of classes.
The Magical Transformation Block
Something that I haven’t yet talked about, but which I’ve used in the examples, is the block passed to grep. This acts much like the block in Enumerable#map, taking a member of the array and returning it transformed in some way. Above I’ve made Strings uppercase and turned dates into more readable Strings, but this block can be as complex as you might need.
Making Your Own Classes “Grep-Friendly”
If you have classes which you might want to use with grep, all you need to do is implement an intelligent === method for whatever you will be passing to grep. In fact, as an example I decided to implement a Magic class which takes a block for use in the === method:
class Magic
def initialize(&block)
@block = block
end
def ===(other)
@block.call(other)
end
end
class Animal < Struct.new(:name, :sound, :class)
def to_s
"#{name}'s go '#{sound}'"
end
end
animals = [
Animal.new("Cow", "Moo!", "Mammal"),
Animal.new("Snake", "Hiss!", "Reptile"),
Animal.new("Dog", "Bark!", "Mammal"),
Animal.new("Eagle", "Go America!", "Bird"),
Animal.new("Cat", "Meow!", "Mammal"),
Animal.new("Shark", "Da Dum, Da Dum, Da Dum!", "Fish")
]
puts animals.grep(Magic.new {|a| a.class == "Mammal"})
# Results in:
# Cow's go 'Moo!'
# Dog's go 'Bark!'
# Cat's go 'Meow!'
Conclusion
I hope this relatively brief look into the Ruby Core was informative and will help any readers produce more elegant and maintainable Ruby code in their applications. As I find other interesting methods and uses I’ll post about them.


Not to be a debbie downer, but why not use select for something like this?
animals.select{|a| a.class == "Mammal"}
I dunno, maybe if you could do something useful with it by adding a block?
Yeah the only reason to do something like that would be to get the combination of select and map in one operation, though they obviously aren't that hard to chain.
I could probably come up with a better example, but the main point was to show how one could make their own === method.
How about this:
mammal_sounds = animals.grep(Magic.new {|a| a.class == "Mammal"}) {|m| m.sound}
:)
Nice stuff Ryan. I think this is a great response to the "What do you want to read?" thread from last week. Welcome back to the posting fold. I hope to see more like this soon.
I think this is a great response to the "What do you want to read?" thread from last week
+1. Well, it's at least thing kind of thing I want to read!
Nice write up Ryan :) This is definitely the kind of content I prefer to see. Little micro slices of usefullness (rather than "this link helped me learn to tie my ruby shoes").
Keep 'em comin'.
Oh, in that case, it'd be cool to demonstrate how you can use === with the case statement.
Yeah Danno I was thinking of writing another brief article about that actually :)
This blog has missed you very much Ryan. Welcome back and way to make an entrance!