29
loading...
This website collects cookies to deliver better user experience
for
loop or the .each
method to iterate over the items in them. We know the for
loop is a form of flow-control but where does this .each
method come from ? You might think it comes from whichever Class the list belongs to but in fact, it is an inherited method from the Enumerable
module. Enumerable
module. .each
, which needs to be implemented in any Class it's included in. Other important methods like .map
, .reduce
and .select
that rely on the implementation of the .each
method to function can then be used for free..each
method will execute the block for each of the array's elements:nums = [1,2,3] # An Array is an Enumerable
nums.each { |i| puts "* #{i}" }
# => 1
# => 2
# => 3
Enumerator.new
or by calling an instance method of an Enumerable
object. So if we call the .each
method on an array without passing a block to execute for each of its elements, we'll receive an instance of Enumerator
. Sounds a bit confusing I know. Lets simplify with a basic example:nums = [1,2,3]
puts nums.each
# => <Enumerator: 0x00007fa3657f90f8>
for
loop can also be used to iterate over a list. So why do we need to use the methods provided by the Enumerable
module? Well, you see when we use a for
loop we risk introducing a bug into our code by inadvertently overwriting a previously declared variable's value. fighter = 'Jackie'
fighters_list = ['Bruce', 'Rocky', 'Rambo']
for fighter in fighters_list
puts fighter
end
puts "Your Fighter has changed => " + fighter # Unintended change
=> Bruce
=> Rocky
=> Rambo
=> Your Fighter has changed => Rambo
Enumerable#each
can be pretty handy. If we were to use the following code, the value of the "fighter" variable would remain unchanged.fighters_list.each { |fighter| puts fighter }
.each
method provided by an Enumerable to iterate over it. There are also many other common things we wish to do with lists such as reduce a list down to a single value like its sum, modify the whole list, and write our own custom logic to iterate over a list if we want to protect some data on the client-side. We can do all of that and more if we use an Enumerable
. We will discuss usage in detail in just a bit.Enumerable#map
method seems like a good first thought. However, this can modify the list but cannot track indexes of individual elements.nums_enum = [1, 2, 3].map
nums_enum.each { |num| puts num }
# => 1
# => 2
# => 3
Enumerable#each_with_index
method. This won't modify the list but we can at least track indexes with it. You can already see where I am going with this. Yes indeed with the help of Enumerators
we can chain these two together to create our very own "Map with Index"-like function call ! Sample below:nums_enum = [1, 2, 3].map # Called without a block so returns #Enumerator
# => #<Enumerator: ...>
p nums_enum.each_with_index{ |n, i| n * i } # Called with block so will iterate with index
# => [0, 2, 6]
p [1,2,3].map.each_with_index{ |n, i| n * i } # Shortened to a single line
# => [0, 2, 6]
10.times.reverse_each.cycle.first(11)
=> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9]
.next
method and you can call this on an Enumerator as well:nums_enum = [1, 2].each
nums_enum.next
# => 1
nums_enum.next
# => 2
nums_enum.next
# => `next': iteration reached an end (StopIteration)
digits = (1..10).to_a # Range to Array
def odd_digits(digits)
index = -1
Enumerator.new do |yielder|
loop do
index += 1
yielder << digits[index] if digits[index] % 2 == 0
end
end
end
$> puts odd_digits(digits).first(4)
=> 2
=> 4
=> 6
=> 8
Enumerable
module. Secondly, we need to implement the each
method in the class to iterate over the elements. Most cases allow falling back to the each
method of another object such as an array.Array#each
method to iterate over the nodes.class LinkedList
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def add(item)
LinkedList.new(item, self)
end
end
LinkedList.new(0).add(5).add(10)
# => <LinkedList:0x1 @head=10, @tail=<LinkedList:0x2 @head=5, @tail=<LinkedList:0x3 @head=0, @tail=nil>>>
class LinkedList include Enumerable # We inherit enumerable methods that use .each
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def add(item)
LinkedList.new(item, self)
end
def each(&block) # Implement our custom each
if block_given?
block.call(@head)
@tail.each(&block) if @tail
else
to_enum(:each) # Return enumerator if block not provided
end
end
end
Enumerable
is self-explanatory by this point. More importantly, we implement the .each
method to let other enumerable methods know how to iterate our linked list..each
method if a block is given we simply call the block on the current node and recursively call .each on the rest of the list until the final node is nil
. If a block was not provided then return an Enumerator that uses our .each method. This last bit will allow the chaining of methods discussed in an earlier use case.$> linked_list = LinkedList.new(1).add(5).add(10)
$> linked_list.each{ |node| puts node }
=> 0
=> 5
=> 10
$> linked_list.select{ |node| node % 5 == 0 } # Select if divisible by 5
=> [10, 5]
.map
and .select
as shown in the example above.