Enumerating Enumerable: Enumerable#grep

by Joey deVilla on August 21, 2008

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:

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.

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:

Swift

1

2

3

4

5

6

7

8

#These are the years when the band Radiohead released an album

radiohead_album_years=[1993,1995,1997,2000,2003,2007]

=>[1993,1995,1997,2000,2003,2007]

#Andthese are the years when Radiohead released an album between1996and

#2002inclusive

radiohead_album_years.grep((1996..2002))

=>[1997,2000]

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:

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

#Identical arrays

[1,2]===[1,2]

=>true

#How about the first arrayasasubset of the second?

[1]===[1,2]

=>false

#How about the first arrayasasuperset of the second?

[1,2,3]===[1,2]

=>false

#How about one arrayasapermutation of the other?

[2,1]===[1,2]

=>false

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.

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:

Swift

1

2

3

4

#Of the countries'total areas,which the ones between

#500,000and1million square km?

total_country_areas.values.grep((500_000..1_000_000))

=>[647500,547030]

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!