Iterators and Enumerable Objects
Although loops constitute a significant part of the Ruby language for defining program control structure, it is probably more common to write loops rather than using unique methods called iterators. Iterators in ruby are one of the most noteworthy features of Ruby and help us in defining the control structure of ruby. We use the term iterator to mean any yield statement method. They do not serve the purpose of an iteration or looping function. For example, data structures like Array, Hash, Range, and several other classes define each iterator that passes each collection element to the associated block. Iterator is a concept of Object-Oriented Programming Language and, in simple terms, often defined as methods supported by collections such as an array, hashes etc. Iterator in ruby returns all the laments one after the other.
Similarly, an Enumerable object is one whose purpose is to enumerate over some other object. Now, after a brief understanding of iterators and enumerable objects in ruby, let's get to the main discussion of how ruby allows Looping through Multiple iterables in parallel. Now down the section, we will be getting to know code implementation and a brief discussion on looping through multiple iterables in parallel.
Looping Through Multiple Iterables in Parallel
We want to traverse multiple iteration methods simultaneously to match the corresponding elements in different arrays. Iteration over arrays is made simpler using The REXML::SyncEnumerator class, defined in the REXML library. Each method yields a series of the array, and each array contains one item from each underlying Enumerable object.
We all know that any object which implements each method can be wrapped in an Enumerator object. Objects help us track where we are in a particular iteration over an entire data structure. Usually, while passing a block into the iterator method, the block passed would get called for every element in the iterator without interruption. It will not run any code that is outside the block and would run till the time iterator is done iterating over it.
The iterator does allow us to stop the iteration by writing the break statement in the code block. Still, the enumerator also allows us to restart from the broken iteration, which the iterator does allow. We could consider an iterator as a vending machine for candies that will dispense all of its candy in a steady stream once we push the button(a faulty one). But the Enumerator class lets us turn that candy dispenser into one that dispenses only one piece of candy every time we push its button.
Now for looping through multiple iterables in parallel, we should first look into the example below, which will help us understand how an array/collection is wrapped using an enumerator.
So consider an array.
sample_array = ["Hello1", 1, "Hello2", 2, "Hello3", "Hello4", 3, "Hello5"]
You can also try this code with Online Ruby Compiler
Run Code
We want to use the functionality of an enumerator to iterate over an array but not the usual way of iteration here; we want to skip Hello entries.
So if you thought wrapping the list in an enumerator would be sufficient, you are partially wrong. It will print the entire list, including hello values.
CODE
sample_array = ["Hello1", 1, "Hello2", 2, "Hello3", "Hello4", 3, "Hello5"]
g = Enumerator.new do |yielder|
count = 0
loop do
yielder.yield sample_array[count]
count += 1
end
end
puts g.next # Hello1
puts g.next # 1
puts g.next # Hello2
You can also try this code with Online Ruby Compiler
Run Code
OUTPUT
Hello1
1
Hello2
We could quickly write an iterator that iterates over the array the way we want it to, but we wanted an enumerator object. So we could wrap the array in an Enumerator or a REXML::SyncEnumerable object. While wrapping the array, we are wrapping the array's each method.
If we could define an appropriate code block and pass it to the Enumerator constructor, we could make a generation object out of any piece of iteration code. The enumerator will then know to call and interrupt that block of code, just as it knows to call and interrupt each when we pass the array into the constructor.
Here is the enumerator that could iterate over the array just the way we want it to
g = Enumerator.new { |g| my_array.my_iterator { |e| g.yield e } }
g.next # => 1
g.next # => 2
g.next # => 3
You can also try this code with Online Ruby Compiler
Run Code
Now, look for the below code for implementing our above learning to use interosculate methods that would wrap methods.
CODE
def interosculate(*iteratables)
generators = iteratables.collect do |x|
Enumerator.new { |g| x.each { |e| g.yield e } }
end
done = false
until done
generators.each do |g|
begin
if item = g.next
yield item
done = false
end
rescue StopIteration
done = true
end
end
end
end
words1 = %w{We Know Ninja}
words2 = %w{Best is Coding All}
interosculate(words1, words2.reverse) { |x| puts x }
You can also try this code with Online Ruby Compiler
Run Code
OUTPUT
We
All
Know
Coding
Ninja
is
Best
You can also try this code with Online Ruby Compiler
Run Code
In the above program, we used an interosculate method that wraps another method. The interosculate method accepts any combination of Enumerable objects and Method objects. It turns each of them into an Enumerator object and loops through all Enumerable objects. Thus able to get one element at a time from each. In the above program, we pass the interosculate method, an array, and a Method object, allowing us to iterate through two arrays in opposite directions.
Analysis
Any object that implements each method can be wrapped in an Enumerator object. If you've used Java, think of an Enumerator as a Java Iterator object. It maintains track of where you are in a particular iteration over a data structure.
Generally, when we pass a block into an iterator method like "each", that block gets called for every element in the iterator without any interruption. No code outside the block will run until the iterator is done iterating. We can stop the iteration by writing a break statement inside the code block. Still, we can't restart a broken iteration afterwords from the same place unless we use an Enumerator.
Think of an iterator method as a candy dispenser that pours all its candy in a steady stream once we push the button. The Enumerator class lets us turn that candy dispenser into one that dispenses only one piece of candy every time we push its button. We can carry this new dispenser around and ration your candy more easily.
Frequently Asked Questions
What is Ruby?
Ruby is an open-source dynamic language with natural and easy-to-read/write syntax. Ruby is a careful balance language and a perfect blend of five different languages.
What are enumerators in ruby?
An enumerator in ruby is a class that allows iterations of both types – external and internal. Internal iteration is when the class in question controls iteration, while external iteration is when the environment or the client controls iteration.
Mention Some of the iterators in Ruby
Iterators in ruby help us in defining the control structure of the program. Each, Times, Upto, Downto, Step, and Each_Line are iterators in ruby.
What is an iterator in ruby?
Iterators in ruby are one of the most noteworthy features of Ruby and help us in defining the control structure of ruby. Iterator in ruby passes each collection element to the associated block.
How does ruby allow looping through multiple iterables in parallel in ruby?
We use an interosculate method for looping through multiple iterables in parallel in ruby. The interosculate method accepts any combination of Enumerable objects and Method objects. It turns each of them into an Enumerator object and loops through all Enumerable objects. Thus able to get one element at a time from each. We then pass the interosculate method, an array, and a Method object, allowing us to iterate through two arrays in opposite directions.
Conclusion
In this article, we have studied Looping through Multiple Iterables in Parallel in Ruby in detail and briefly explored Looping using Multiple Iterables in Parallel with the code implementation. We expect that this article must have helped you enhance your knowledge on the topic of Ruby. Give an upvote to the blog to help other ninjas grow.
Also, visit our Guided Path in Coding Ninjas Studio to learn about Ruby. If you are preparing for an interview, visit our Interview Experience Section and interview bundle for placement preparations. Upskill yourself in Ruby on Rails, Backend Web Technologies, Hadoop, SQL, MongoDB, Data Structures and Algorithms, JavaScript, System Design, and much more!. Please upvote our blogs if you find them engaging and helpful! I wish you all the best for your future adventures and Happy Coding.
Useful links: Operational Databases, Non- relational databases, MongoDB, Top-100-SQL-problems, interview-experience, Introduction to ruby on rails, Directory Structure in Ruby, Ruby on Rails, Ruby vs Python. You can also refer to the Official Ruby, Ruby, Official Documentation, Ruby FAQ, Ruby Koans, Ruby Doc, Why Ruby?