Introduction to Enumerable

What are you going to learn?

  • Understand the most common enumerable methods for collections - map, reduce, select, find
  • Be able to understand how Ruby Docs is organized and to search for methods
  • Correctly utilize the basic syntax for enumerables in Ruby

What is Enumerable?

Back in the Introduction to Each lesson, we learn how to iterate over collections(arrays or hash) and also perform some operations on them, such as value sum, filtering elements and so on. Due to this to the fact that this operations are very common when coding, Ruby comes with a built-in methods that help us to work with the most common iteration patterns. This methods come from the enumerable module.

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

Before jumping into the enumerable methods, here is a reminder on how the each iterator looks:

[1,2,3,4].each do |number|
 puts number
end
#=> [1,2,3,4]

Note: Remember that the each iterator returns the original collection, not the actual process inside of it

map

By the official definition:

Returns a new array with the results of running block once for every element in enum.

In other words, the difference between map and each, is that the first one returns whatever we do in the block.

Take the following code for example:

numbers = [1,2,3,4,5]
squared_numbers = []

numbers.each do |number|
  squared_numbers << number**2
end

puts squared_numbers
#=> [1,4,9,16,25]

This is simpler with map:

numbers = [1,2,3,4,5]

squared_numbers = numbers.map do |number|
  number**2
end

puts squared_numbers
#=> [1,4,9,16,25]

As you can see, we are no longer appending elements to the squared_numbers, instead of that, we are directly assigning it. So whenever you have this situation with each where you are saving values into another empty collection, it's probably better if you go with a map enumerable.

reduce

Reduce will combine all of the elements on the collection and reduce it to a single result. A clear example of this is to reduce the elements from an array by summing them all.

Take for example the following code with each:

numbers = [1,2,3,4,5]
result = 0

numbers.each do |number|
  result = result + number
end

puts result
#=> 15

Now, with reduce:

numbers = [1,2,3,4,5]
result = numbers.reduce(:+)

puts result
#=> 15

As you can see we are introducing what is called, sugar syntax, but you can also use the full version:

numbers = [1,2,3,4,5]
result = numbers.reduce do |sum, number|
  sum + number
end

# This is the short version for the block, works exactly the same. This is preferred when only one operation in being performed
#result = numbers.reduce { |sum, number| sum + number }

puts result
#=> 15

We know, there could be a bit of confusion around this, but here are some other examples:

# Multiply some numbers
(5..10).reduce(1, :*)                          #=> 151200

# find the longest word
longest = %w{ cat sheep bear }.reduce do |memo, word|
   memo.length > word.length ? memo : word
end

longest      

find

As you can infer by the name, the find method, helps you locate the first element which meet the search criteria.

Take for example the following code with each:

numbers = [1,2,3,4,5]
even = nil

numbers.each do |number|
  return even = number if number.even?
end

puts even
#=> 2

Now, with find:

numbers = [1,2,3,4,5]
even = numbers.find do |number|
  number.even?
end

puts even
#=> 2

As you can see, even though there are two numbers that meet the search criteria, even?, the method find will always return the first match.

select

What if instead of just returning the first matching element you want all?, this is where select comes into play. By the official definition:

Returns an array containing all elements of enum for which the given block returns a true value.

Take for example the following code with each:

numbers = [1,2,3,4,5]
even_numbers = []

numbers.each do |number|
  even_numbers << number if number.even?
end

puts even_numbers
#=> [2,4]

This is how it looks with select:

numbers = [1,2,3,4,5]

even_numbers = numbers.select do |number|
  number.even?
end

# This are two alternatives which works the same
# even_numbers = numbers.select { |number| number.even? }
# even_numbers = numbers.select(&:even?) What is this? => https://blog.pjam.me/posts/ruby-symbol-to-proc-the-short-version/

puts even_numbers
#=> [2,4]

More enumerable methods

There are a lot more enumerables out there to achieve many tasks in simple steps. A good place to start searching are the Ruby Docs, but here is a list of what we think may come in handy someday:

  • #reject
  • #grep
  • #include?
  • #none?
  • #one?
  • #max
  • #min
  • #group_by

Exercises

Remember we have provided a repository with a bunch of exercises for you to complete. You can find it here

You can finde them under /ruby-exercises/Module1/enumerables.

Additional Resources