Your Friend Enumerable

Everyone seems to hate interviews. Folks stress about them. They memorize piles of algorithms, complexities, and data structures. Personally, I do not think that helps. What does help is knowing the string and collections libraries for your language backward, forwards, and inside out.

A lot of Rubyists do not know much of enumerable beyond each. Here are a handful of methods that are handy to know both for interviews and general purpose coding.

find

(1..10).find { |i| i % 3 = 0 }   #=> 3

('a'..'z').find { |c| c == "Z" }  #=> nil

(1..10).find(-> {"Not Found"}) { |i| i > 15 } #=> "Not Found"

Find runs the block on each element of the collection in turn. It returns the first element that makes the block true. It has an optional argument ifnone that is called if no element makes the block true. In the third example above, I use stabby-proc syntax to return “Not Found” in this case. Find is aliased as detect.

select

(1..10).select { |i| i % 3 == 0 } #=> [3, 6, 9]

{a: 1, b: 2, c: 3, d: 4}.select { |k, v| v % 2 == 0} #=> {:b=>2, :d=>4}

Like find, select runs the block on each element of the collection. Instead of stopping the first time the block returns true select keeps going and returns all the values that make the block true. Like all enumerable methods select can be used on Arrays, Hashes, Ranges, Sets, and any other class than mixes in the Enumerable module. Select can also be called with find_all.

reject

(1..10).reject { |i| i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10]

{a: 1, b: 2, c: 3, d: 4}.reject { |k, v| v % 2 == 0} #=> {:a=>1, :c=>3}

Reject is the opposite of select. It runs the block on each element in turn and returns the ones where the block evaluates to false.

all? any? one? none?

(1..5).all? { |i| i < 6}  #=> true
(1..5).all? { |i| i < 2}  #=> false

(1..5).any? { |i| i < 2}  #=> true

(1..5).one? { |i| i < 2}  #=> true

(1..5).none? { |i| i < 5} #=> false

I used to use loops with each, while, or until to implement these. Learning that they are built into enumerable makes me less prone to error. By Ruby convention all of these end in a question mark and so they are predicate methods; they return either true or false. All? returns true only if the block returns true for every element in the collection. Any? passes an element to the block in turn. When an element makes the block true the call to any? returns true and the rest of the elements in the collection are not checked. Only one star is printed in the following example.

(1..5).any? { |i| puts "*"; i < 2}  #=> true
*

One? returns true if and only if exactly one element makes the block return true. It returns when all the elements have been checked or when it first detects two elements that make the block true.

(1..5).one? { |i| puts "*"; i < 3} #=> false
*
*

Lastly, none? returns true if none of the elements in the block make the collection true. It will return early if an element makes the block true. Otherwise, it must evaluate all the elements in the collection.

group_by

(1..6).group_by { |i| i.even? }
#=>  {false=>[1, 3, 5], true=>[2, 4, 6]}

{a: 1, b: 3, c: 3, d: 2, e: 1}.group_by { |_, k| k }
#=> {1=>[[:a, 1], [:e, 1]], 3=>[[:b, 3], [:c, 3]], 2=>[[:d, 2]]}

Group_by passes each element of the collection to the block and returns a hash where the keys are return values from the block, and the values are a list of elements that made the block evaluate to that value.

When run against a Hash this returns rested tuples instead of a hash as the values. To convert the tuples back to a hash you can use the Hash::[] class method.

hsh = {1=>[[:a, 1], [:e, 1]], 3=>[[:b, 3], [:c, 3]], 2=>[[:d, 2]]}

hsh.each { |k, v| hsh[k] = Hash[v] }

#=> {1=>{:a=>1, :e=>1}, 3=>{:b=>3, :c=>3}, 2=>{:d=>2}}

each_cons

(1..5).each_cons(2) { |a| p a }

# outputs below
[1, 2]
[2, 3]
[3, 4]
[4, 5]

Each_cons runs a sliding window over the collection. The argument to each_cons is the size of the window, and the values in the window are passed to the block. For the folks who remembered to use it, this was handy during Seattle.rb’s Markov chain exercise. You can either treat the values as one variable (like I did above), or you can explode them out like below.

(1..8).each_cons(4) { |a, _, _, d| p "#{a}, #{d}" }

# outputs below
"1, 4"
"2, 5"
"3, 6"
"4, 7"
"5, 8"

Every window each_cons uses in the block will contain the same number of elements. There won’t be a partial “window” sent to the block.

each_slice

%w[a b c d e].each_slice(2) { |a| p a }

# outputs below
["a", "b"]
["c", "d"]
["e"]

This is another one that I spent several hours implementing by hand before someone pointed me at the built-in version. I was trying to render a grid of elements in Rails. Each row had five items, and the last row might have fewer depending on the overall count. I had two or three nested loops to do it. Each_slice is much cleaner. Unlike each_cons, each_slice might return a “partial window” if the collection is not evenly divisible by the argument passed to each slice.

Enumerable is your friend

There’s a lot more in enumerable that can be handy. For me, enumerable is a great example of the Ruby philosophy that programming should be fun. A lot of the common things you want to do with collections are automatically included. Better yet if you include the Enumerable module, your collection objects get these methods as well.