Sandip Mane
by Sandip Mane
2 min read

Categories

  • Ruby

Tags

  • module
  • ruby

Ruby’s Enumerable module adds magical methods to classes like Array and Hash. In this post, we will learn to implement enumeration with an example.

Enumeration

Enumeration is a process of traversing over objects one by one. To make a class enumerable, we can include Enumerable module. Thus, giving access to methods like #map, #filter, #count, #include?, #any?, #uniq and a lot more! Check the full list here.

#each

Enumerable module mainly relies on #each method on the implementing class. The #each method is implemented such that when a block is passed, the contents of the collection are evaluated.

> [1, 2, 3].each { |a| a }
=> [1, 2, 3]

Enumerator

Enumerator is a class which allows manual iteration over an enumerator object. When #each is called without a block, it returns a new Enumerator object hence allowing to chain multiple enumerators.

> enumerator = %w[foo bar baz].each
> enumerator
=> #<Enumerator: ["foo", "bar", "baz"]:each>

> enumerator.map.with_index { |w, i| "#{i}:#{w}" }
=> ["0:foo", "1:bar", "2:baz"]

Example

Let us create a linked list in ruby.

Class Node represents single item in the linked list, this will hold reference/link to the next item in the list.

class Node
  attr_accessor :value, :next

  def initialize(value)
    @value = value
  end

  def inspect
    { @value => @next }
  end
end

Class LinkedList will hold reference to the first/head item in the list. This class will also be responsible for manipulating the list. (For this example we are simply going to use it for inserting values in the list)

class LinkedList
  def <<(value)
    if @head
      tail.next = Node.new(value)
    else
      @head = Node.new(value)
    end
  end

  def tail
    item = @head

    return item if item.next.nil?
    return item if item.next.nil? while (item = item.next)
  end

  def inspect
    [@head].inspect
  end
end

Awesome! Now we can create linked list

> list = LinkedList.new
> list << 10
> list << 20
> list << 30

> puts list.inspect
=> [{ 10 => { 20 => { 30 => nil } } }]

Making LinkedList Enumerable

Include Enumerable module and add #each method in the LinkedList class.

class LinkedList
  include Enumerable
  .
  .
  .
  def each(&block)
    item = @head
    block.call(item.value)
    block.call(item.value) while (item = item.next)
  end
end

Done! Let’s try a few methods from Enumerable module

> list = LinkedList.new
> list << 30
> list << 20
> list << 10

> list.sort         # => [10, 20, 30]
> list.count        # => 3
> list.include? 10  # => true

It works!😎 Let’s try a few more.

> list.minmax         # => [10, 30]
> list.sum            # => 60

> list << 20
> list.uniq           # => [30, 20, 10]

> list.filter { |value| value < 25 }
# => [20, 10, 20]

> list << 51
> list.select(&:odd?) # => [51]

Next, when the block is not passed to each, we should return an enumerator object so that the enumerators can be chained.

> list.each
# => NoMethodError (undefined method `call' for nil:NilClass)

To do this, change the #each method as following

def each(&block)
  if block_given?
    item = @head
    block.call(item.value)
    block.call(item.value) while (item = item.next)
  else
    to_enum(:each)
  end
end

Now, we should be able to chain enumerator methods

> list = LinkedList.new
> list << 10
> list << 20
> list << 30

> list.each
=> #<Enumerator: [{10=>{20=>{30=>nil}}}]:each>

> list.each.with_index.to_h
=> { 10=>0, 20=>1, 30=>2 }

Finally,

In this post, we saw how we can add magical Enumerable methods to classes with just include and an #each method.

Here’s final code for both the classes:

And as always, thanks for reading!😃