Dissecting Ruby

Enumerable — Implementing Find

Do you ever wonder what is going on behind the scenes when you code? This is something I think about a lot. I am a ruby developer who has depended on different methods in ruby without actually knowing how it is implemented.

I am here to discuss a few of them in detail, or well, dissect some of them. Rather than outline the original C implementation of methods, I’ll try my best to describe it in detail from Ruby’s point of view.

It’s really hard to survive in the Ruby world without using collections and methods on them. One such method is . It returns the first element which satisfies the given condition in the block. If there is no block, then it returns the enumerator itself.

marks = [40, 60, 34, 70]
marks.find{|a| a < 40}
=> 34
marks_by_subject = {
english: 40, maths: 60, physics: 34, chemistry: 70
}
marks_by_subject.find{|key,value| value < 40}
=> [:physics, 34]
divisible_range = 1..5
divisible_range.find{|num| num % 2 == 0 }
=> 2

Like the method, there are other common methods every collection has, which originates from the enumerable module. It’s the Ruby way to put the common behavior used by different classes into a module and mixin those modules with classes.

The Enumerable mixin provides collection classes with several traversal and searching methods, and with the ability to sort.

For classes to use the Enumerable module, it has to define an instance method named . The method is supposed to serve the block with each element. If method is not defined inside the class, trying to use enumerable methods will throw an .

To serve the block with each element, we need to use yield. is a keyword in Ruby which can be used to call the block. The return value of is the block’s return value.

class Week
include Enumerable
def each
yield "Monday"
yield "Tuesday"
yield "Wednesday"
yield "Thursday"
yield "Friday"
yield "Saturday"
yield "Sunday"
end
end
week = Week.new
=> #<Week:0x0000559fe6b79a40>
week.include?("Friday")
=> true
week.find {|day| day.start_with?('Sat') }
=> "Saturday"

Even though we have not defined the methods like or in the class Week, the Enumerable module and each method helps us to achieve this function.

To understand this better, I thought of implementing the and method in ruby.

I opened the array class and created my version of each, . It’s a simple method which goes through each element in an array by index with the help of a while loop and serves it to the block.

class Array
def my_each
return to_enum(:my_each) unless block_given?
i = 0
while i < size
yield self[i]
i += 1
end
self
end
end
marks = [40, 60, 77, 81, 67]
marks.my_each{|mark| p mark} # will print all elements in array

It’s a common practice to return an Enumerator when they’re called without a block. Enumerators can be used to chain methods. In simple, Enumerators are similar to iterators but instead of being a method, it’s an object with a state that can track its own progress. Talking about Enumerators could be an article in itself, so lets not get into that now.

The next step is to create a method which will get a value from the method. This value is then passed to the block which is provided to the method. If the block returns true, we break from and return the element found.

module MyEnumerable
def my_find failure = nil
return to_enum(:my_find) unless block_given?
element_found = nil
is_found = false
each do |num|
if yield num
element_found = num
is_found = true
break
end
end
unless (is_found || failure.nil?)
failure.call
else
element_found
end
end
end
class Array
include MyEnumerable
end

This code has created a module which has a method that is included in the class. The optional argument in the method will be a lambda/proc object which will be called if the find operation fails.

failure = lambda { "No Distinction" }marks = [40, 60, 77, 81, 67]marks.my_find(failure) {|mark| mark > 90 }
=> "No Distinction"
marks.my_find{|mark| mark.equal? 77 }
=> 77

It’s not just arrays, we can use the method we created in all the classes which have an each method.

class Hash
include MyEnumerable
end
marks_by_subject = {
english: 40, maths: 60, physics: 34, chemistry: 70
}
marks_by_subject.my_find{|key,value| value < 40}
=> [:physics, 34]
class Week
include MyEnumerable
end
week.my_find {|day| day.start_with?('Sat') }
=> "Saturday"

We can create all the Enumerable methods on top of each method like this.

By creating your own find and each methods, we’re able to recognize that under the hood, all Enumerable methods use the each method. Please let me know what you think, your feedback is appreciated.

--

--

End to End Product Developer with an immense passion for Rails and React. https://github.com/shanshaji

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store