Enumerating Enumerable: Enumerable#grep

by Joey deVilla on August 21, 2008

Enumerating Enumerable

Once again, it’s Enumerating Enumerable time! This is the latest in my series of articles where I set out to make better documentation for Ruby’s Enumerable module than Ruby-Doc.org’s. In this installment, I cover the grep method.

In case you missed any of the previous articles, they’re listed and linked below:

  1. all?
  2. any?
  3. collect / map
  4. count
  5. cycle
  6. detect / find
  7. drop
  8. drop_while
  9. each_cons
  10. each_slice
  11. each_with_index
  12. entries / to_a
  13. find_all / select
  14. find_index
  15. first

Enumerable#first Quick Summary

Graphic representation of the "grep" method in Ruby's "Enumerable" module

In the simplest possible terms Which items in the collection are a === match for a given value?
Ruby version 1.8 and 1.9
Expects
  • An argument against which every object in the collection will be compared using the === operator.
  • (Optional) A block to be used in a map operation on the resulting array.
Returns
  • If no block is given, an array containing the items in the collection that were a === for the given argument.
  • If a block is given, an array containing the items in the collection that were a === for the given argument, which is then mapped using the block.
RubyDoc.org’s entry Enumerable#grep

Enumerable#grep, Regular Expressions and Arrays

The grep method’s name implies regular expressions, and that’s one of its uses. When given a regular expression as an argument and used without a block, grep returns an array containing the items in the original array that match the given regular expression.

When a block is used with grep, the contents of the result array are passed through the block and the resulting array is returned. Think of it as grep followed by a collect/map operation.

What Enumerable#grep Really Does: The === Operator

Here’s grep‘s secret: what it actually does is take each item in the array, compares it against the given argument using Ruby’s === (the “triple equals”) operator and returns an array of those items in the original array for which the comparison returns true.

For regular expressions, the === operator is grep-like. The expression r === s operator returns true if there is a match for regular expression r in string s.

Different classes implement === differently. For example, in the Range class, === is used to see if an item is within the range. The expression r === x returns true if x is in range r. Here’s grep in action when its argument is a range:

Generally speaking, collection.grep(thing_to_compare) compares thing_to_compare with each item in collection using the === operator as defined for thing_to_compare‘s class. It returns an array of those items in the original array for which the comparison returned true.

Don’t forget the extra processing — a map operation — comes “free” if you provide grep with a block:

Enumerable#grep and Hashes

I’ll put it simply: Enumerable#grep isn’t terribly useful with hashes. Like most methods of Enumerable, when applied to a hash, grep, as it iterates through the hash, converts each key-value pair into a two-element array where the first element is the key and the second element is the corresponding value.

As I mentioned earlier, grep uses the === operator to do its comparison, and for arrays, === returns true only when comparing identical arrays:

The practical upshot of all this is that for hashes, grep will return the empty array [] for most arguments, with the notable exception of an argument that is a two-dimensional array that corresponds to one of the key-value pairs in the hash.

That was a bit wordy, but an example should clear things right up:

Making Hashes grep-able

If you need to find which keys in a hatch pattern-match a given value, use the Hash#keys method (which returns an array of the hash’s keys) and grep that:

If you need to-find which values in a hatch pattern-match a given value, use the Hash#values method (which returns an array of the hash’s values) and grep that:

What if you want to find key-value pairs where either the key or the value is a === match for a given argument? There’s a way to do that, and I’ll cover it when we get to the Enumerable#inject method. It’ll be soon, I promise!

Previous post:

Next post: