CodeGnome Consulting, LTD

Programming - DevOps - Project Management - Information Security

Enumerator#each_cons

| Comments

If you are a Ruby programmer and you haven’t found a use for Enumerable#each_cons, you just probably haven’t thought about it yet.
     — Michael Feathers (@mfeathers)

Well, Ruby is a great language, with a lot of nice surprises tucked away in its dustier corners. The Enumerator class is certainly a common one to reach for when solving problems, but I can honestly say I’ve never needed the Enumerator#each_cons method. After seeing Michael’s tweet, I took a look to see what it does.

The method definition says:

each_cons(n) {...} → nil
each_cons(n) → an_enumerator

Iterates the given block for each array of consecutive <n> elements.
If no block is given, returns an enumerator.

On the surface, it looks like just another way to specify an array slice. Not so! The #each_slice method will return chunks of n elements, but #each_cons actually returns a sliding window of elements. Consider the following:

#each_cons vs. #each_slice
1
2
3
4
5
6
7
8
9
10
[1] pry(main)> (1..5).each_cons(3) { |cons| p cons }
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
=> nil

[2] pry(main)> (1..5).each_slice(3) { |slice| p slice }
[1, 2, 3]
[4, 5]
=> nil

What’s interesting is that the output of #each_slice will return non-overlapping blocks of Enumerable elements, while #each_cons shows all possible consecutive blocks from the Enumerable object. It’s as if the method slides a window of n elements across the length of an array, moving the starting index of the window up by exactly one element each time.

Another non-obvious property is that #each_slice will happily return an array smaller than the specified value if it runs out of Enumerable elements to process, while #each_cons always returns the requested length unless it returns nil. For example:

method returns consistently-sized arrays
1
2
3
4
5
6
7
8
[1] pry(main)> (1..7).each_cons(5) { |cons| p cons }
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]
[3, 4, 5, 6, 7]
=> nil

[2] pry(main)> (1..4).each_cons(5) { |cons| p cons }
=> nil

Even though the specified Range object is not a multiple of five, our example always returns exactly five elements in each array unless there are less than five total elements in the Range. Nifty!

So, What’s It Good For?

Maybe it’s a failure of imagination on my part, but I can’t actually think of a useful application for this method outside of validating Fibonacci sequences.

validating a Fibonacci sequence
1
2
fib = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
fib.each_cons(3) { |triplet| p triplet[0] + triplet[1] == triplet[2] }

If you want to know all the various ways that elements of an array can be arranged together, Array#combination or Array#permutation seem like they’d be more intrinsically useful. On the other hand, this method is clearly intended for segmenting an Enumerable, but the real-world usefulness of doing so eludes me.

I always enjoy exploring odd corners of Ruby, and hope you’ve enjoyed this little tour of Enumerable#each_cons. If you can think of a practical use for it, please leave a comment below.

Comments