In order to mix-in the functionality from enumerable module into a Ruby class, the class needs to be a collection and the each method needs to be defined. The each method is responsible for holding the iteration logic for a class. This method defines what value(s) are yielded to the code block during each iteration. The logic encapsulated in this method serves as the basis for all other enumerable module methods, which are briefly described below.
First let’s look at the conditional methods that return boolean values. All of these methods, except include?, will evaluate the return values from the code block to determine whether to return a true or false. The names of these methods make their function pretty self explanatory.
array = ["sparkfun", "adafruit", "digikey", "seed studio", "mouser", "jameco"]p array.all? {|cur| cur =~ /a/}p array.any? {|cur| cur =~ / /}p array.one? {|cur| cur =~ /m/}p array.none? {|cur| cur =~ /z/}p array.include?("sparkfun")Next let’s look at the methods that return filtered content from a collection. Once again the code blocks play a key role in how these methods work. All of these methods, except grep, use the return value from the code block to select which elements should be returned. The find method returns only first matching element from the original collection, while the find_all and select methods return all matching elements. The reject method, returns all non-matching element.
The grep method works a little bit differently. The filter logic is defined by a RegEx expression that is passed into the method as an argument. The code block is used to process the matching elements before they are added to the array that is returned. In the example below, the results are converted to uppercase.
array = ["sparkfun", "adafruit", "digikey", "seed studio", "mouser", "jameco"]p array.find {|cur| cur =~ /di/} p array.find_all {|cur| cur =~ /di/}p array.select {|cur| cur =~ /di/}p array.reject {|cur| cur =~ /di/}p array.grep(/.?di./) {|cur| cur.upcase}The group_by and partition methods filter elements by arranging them into separate groups. The group_by method returns a hash table with the elements from the original collection sorted into different groups based on an expression defined in the code block. In the example below the words are grouped based on the length.
The partition array returns elements from the original collection sorted into two groups based on a true/false condition that is applied to each element. In the example below the expression checks whether each word has more than 5 letters.
array = ["hi", "hello", "greetings", "bye", "goodbye"]p array.group_by {|cur| cur.size} p array.partition {|cur| cur.size > 5} There are several interesting variations of the each method that are implemented for all classes mixed in with the enumerable module. These are methods designed to provide access to all elements from a collection via the code block. They differ from the examples we covered because they do not provide a useful return value – they return the original collection, unchanged.
The each_with_index method iterates through collection and yields the current element and index to code block. The cycle(n) method iterates through a collection n number of “times”, yielding the current element from the collection to the code block.
The each_slice(size) method iterates through collection a few elements at a time. It yields to the code block an array that contain a non-overlapping slice of the elements from the collection. The each_cons(size) methods also iterates through a collection by looking at several elements at a time. However, the arrays that it yields to the code block are overlapping.
array = ["sparkfun", "adafruit", "digikey", "seed studio", "mouser", "jameco"]array.each_with_index { |element, ind| puts "#{element} is at position #{ind}"} array.cycle(2) { |element| puts element }array.each_slice(3) { |slice| print "current slice #{slice}\n" } array.each_cons(3) { |cons| print "current cons #{cons}\n" } Lastly, let’s look at methods that return content generated during the iteration process. These methods return single objects, or arrays, that have been created through the iteration process.
The inject(init) method loops through a collection and yields to the code block the current element along with an accumulator. The accumulator can be initialized via an argument; otherwise, it is initialized with the first element from the collection. Once finished iterating the method returns the last statement from the code block, which is usually the accumulator. It is important to remember that the accumulator value that is passed from one iteration to the next is always the value from last expression in the code block.
array = ["sparkfun", "adafruit", "digikey", "seed studio", "mouser", "jameco"]final_count = array.inject(0) do |acc, element| puts "current element #{element}, total letters #{acc += element.length}" acc endputs final_countThe map method is one of the most powerful methods from the each family. It iterates through a collection and generates a new array that is populated with the return values from the code block. This method returns a new array of the same size as the original collection.
array = ["sparkfun", "adafruit", "digikey", "seed studio", "mouser", "jameco"]p array.map {|cur| cur.upcase.reverse}In order to be able to sort custom-created objects in a collection you can take one of three approaches. All approaches require the definition of the logic which governs how a custom-created object should be compared with other objects – from a greater than, equal to, and less than perspective.
The simplest approach is to define a <=> method, also known as a spaceship method. This method accepts an object as an argument. It needs to be defined so that it returns -1 if the self object i less than, 1 if the self object is greater than, and 0 if both objects are equal. Once this method is defined for a given class then Ruby will know how to sort a collection of objects from that class.
The next approach is to use a code block that tells the array how to sort the objects. This approach can be very useful when you have an array of objects that needs to be sorted in many different ways. The sort method passes two different objects to the code block; by defining how to compare two objects the collection is able to sort the entire collection.
[obj_0, obj_ 1, obj_ 2, obj_ 3, obj_ 4, obj_ 5].sort do |obj_a, obj_ b| obj_a.year <=> obj_b.yearend# for illustration only - not functional codeThe last, and most robust approach, is to define the spaceship method and mix-in the Comparable module into the new class. Beyond array sorting, the Comparable module provides your objects with ability to support comparison operations such as <, >, == and so on.