Ruby each_cons

Send to friend

As methods go, each cons is pretty mysterious. I’m not even sure exactly what you would use it for. However, in the interest of science, here is a brief discussion.

UPDATE
Gregory Brown, who wrote the Prawn PDF library, suggests that:

“In general each_cons is useful when you need a sliding window of size n across a dataset.”

/update

Usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Necessary
>> require "enumerator"
=> true

# Theory
>> [*('a'..'g')].each_cons(2){|set| p set.join(" and ")}
"a and b"
"b and c"
"c and d"
"d and e"
"e and f" 
"f and g"
=> nil
# Iterates the given block for each array of consecutive <n> elements. 


# Practice
????????

# Documentation
    (1..10).each_cons(3) {|a| p a}
    # outputs below
    [1, 2, 3]
    [2, 3, 4]
    [3, 4, 5]
    [4, 5, 6]
    [5, 6, 7]
    [6, 7, 8]
    [7, 8, 9]
    [8, 9, 10]

Real life

The Prawn pdf library uses each_cons like so

1
2
3
4
5
6
    def polygon(*points)
      move_to points[0]
      (points << points[0]).each_cons(2) do |p1,p2|
        line_to(*p2)
      end
    end

update

Gregory Brown says:

%Q{

I think you may have been confused by my ugly code there. I have
replaced it with:

1
2
3
4
5
6
    def polygon(*points)
      move_to points[0]
      (points[1..-1] << points[0]).each do |point|
        line_to(*point)
      end
    end

The reason why it’s not needed is because we draw the lines from point
to point.

But if we were drawing them segment by segment, it’d make sense.

1
2
3
4
5
>> [[1,2],[3,4],[5,6],[1,2]].each_cons(2) { |a| p a }
[[1, 2], [3, 4]]
[[3, 4], [5, 6]]
[[5, 6], [1, 2]]

I imagine I had refactored a line p1, p2 call down to just line_to(p2)
without fixing the each_cons()… sorry about that.

In general each_cons is useful when you need a sliding window of size
n across a dataset.

Here’s a more reasonable usage, for solving a simple tree-traversal
problem:
http://blog.majesticseacreature.com/archives/2008.10/euler_67.html

}

As far as I can google, prawn seemed to be the only usage of each_cons in the wild. Now that he’s refactored it, it appears that nothing on github will be using it.

If you’re curious, I’ve posted a question on the ruby mailing list here about what this is actually for.

At any rate, here’s the…

Implementation

1
2
3
4
5
6
7
8
9
10
  def each_cons(n, &block)
    array = []
    elements = self.to_a
    while elements.size > 0 do
      array << elements[0,n] if elements[0,n].size == n
      elements.shift
    end
    array.each { |set| yield set }
    nil
  end

Rubinius begins by creating an empty array, then forcing the object to be an array, if it isn’t already.

Next, it iterates over each element in the enumerable object, adding a sub array of elements with the length of n.

Next, it iterates over each of the new array’s elements, passing the set to the block.

Finally, it returns nil.