Introduction to #each

What are you going to learn?

  • Understand the #each operator to navigate collections
  • Modify collection data through an #each iterator
  • Be able to understand how Ruby Docs is organized and to search for methods
  • Correctly utilize the basic syntax for #each in Ruby

each

#each is an iterator:

  • Supported by all collections in Ruby.
  • It returns all the elements of an array or hash.
  • Will allow us to perform anything for every element of the collection

As you may recall from the arrays and (hash)[hashes] lessons, these are collections that can be accessed through an index value:

fruits = ["Apple", "Banana", "Kiwi"]
fruits[0] #=> "Apple"
fruits[1] #=> "Banana"
fruits[2] #=> "Kiwi"

heroe = {
  name: "Batman",
  real_name: "Bruce Wayne",
  powers: ["Cool gadgets", "shadows", "ninja skills"]
}

heroe[:name] #=> "Batman"
heroe[:powers] #=> ["Cool gadgets", "shadows", "ninja skills"]

A simple implementation for an array to print every element would be:

fruits = ["Apple", "Banana", "Kiwi"]

fruits.each do |fruit|
  puts fruit
end

As you can see .each is a method called on the fruits array.

Everything we do inside the do and end is called Block. Whatever is insde the block, will execute the number of elements on the array. fruit is the block variable that will store the current element we are iterating over.

A simple implementation for a hash to print every pair would be:

heroe = {
  name: "Batman",
  real_name: "Bruce Wayne",
  powers: ["Cool gadgets", "shadows", "ninja skills"]
}

heroe.each do |key, value|
  puts "#{key}: #{value}"
end

This time each method is called on the heroe hash.

A subtle difference with the array, is that instead of having one block variable you have two, in this case one for the key and one for the value.

Rmember that #each will always return the whole collection by the end of the execution

The usage of #each is on of the most common ways to iterate a collection, and certainly the preferred one by the Ruby community. It is used to modify elements from the collection, locate them, remove them, or even create new stuff. Take in account that there are many other methods to manipulate collections, such as find, select, keep_if, map, but don't worry for now, we will cover them in later lessons.

Modify every element

There will be times when you want to modify each element from a collection, and each is certainly a way to do it. Take the next code for example:

fruits = ["apPle", "BanAna", "KiwI"]

fruits.each do |fruit|
  fruit.downcase
end
  • What would be the expected output?
  • Is what you expected?

A common mistake when working with #each is that while iterating and having access to the current value, you may think that by applying some method or operation on the element, it will persist the changes, but this is not the case. Why?, well because the block variables are that, variables that are storing the current value, but not actually performing it on the element, therefor any modification will happen to the variable, not the element. For example:

fruits = ["apPle", "BanAna", "KiwI"]
fruit = fruits[0]
fruit.downcase
# The fruit variable stores what is on the 0 position from the array
# in this case "apPle", and anything you perform on it will only
# affect the fruit variable

In order to get the output you expected, you may want to do something like:

fruits = ["apPle", "BanAna", "KiwI"]
downcased_fruits = []

fruits.each do |fruit|
  downcased_fruits << fruit.downcase
end

puts downcase_fruits
#=> ["apple", "banana", "kiwi"]

Create a new collection

Maybe you wan to create a more readable type of collection, such as a hash from a given array:

fruits = ["apple", "banana", "kiwi"]
fruits_collection = []

fruits.each do |fruit|
  fruits_collection << {
    name: fruit
    quantity: rand(10)
  }
end

puts fruits_collection

In the example above we created a new array full of hash objects, to have a more descriptive data set, and we achieve this by iterating the fruits array and even complementing with a bit more data, in this case the quantity.

Here is another example with a conditional:

numbers = [1,2,3,4,5,6,7,8,9,10]
even_numbers = []

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

puts even_numbers

Make sure you test the examples above in your terminal

Calculate a result

Instead of creating new collection, sometimes you may want to perform a calculation using a collection and #each:

numbers = [1,2,3,4,5,6,7,8,9,10]

sum = 0

numbers.each do |number|
  sum = sum + number
  #sum += number - this also works
end

puts sum

As you can see, we take an array of numbers, iterate and sum each element to get a whole new value. Obviously this is no the only way to achieve this, and certainly the examples presented are not the only usages of #each. You can read more here.

Sugar syntax

As we progress and you become more expert on Ruby, you will find that it offers many handy short versions when calling a method, specially the ones with blocks. Take for example:

array.each do |element|
  puts element
end

You could rewrite this to:

array.each { |element| puts element }

This is highly use in Ruby, but be aware that this is only recommended when the block of code is very short or that can be executed with one line. Otherwise avoid using this if you are performing more than one action on the block.

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/each.

Additional Resources