Ruby Iterators
Ruby Iterators: each, map, and More
Section titled “Ruby Iterators: each, map, and More”Iterators are methods that repeatedly invoke a block of code for each element in a collection, such as an Array or Hash. They are a cornerstone of Ruby’s expressive and concise style for working with collections.
Ruby’s Enumerable module, mixed into classes like Array and Hash, provides a rich set of iterator methods. We’ll focus on each and map (also known as collect), and briefly touch upon others.
The each Iterator:
Section titled “The each Iterator:”The each iterator is one of the most fundamental. It executes a given block once for each element in the collection, passing the element as an argument to the block. each always returns the original collection it was called on.
Syntax:
Section titled “Syntax:”collection.each do |variable| # code to execute for each elementend
# Or for single-line blocks:collection.each { |variable| # code }Example with an Array:
Section titled “Example with an Array:”#!/usr/bin/env ruby
numbers = [1, 2, 3, 4, 5]
puts "Iterating through numbers:"numbers.each do |number| puts "Current number: #{number}"end
# The return value of each is the original arrayreturned_value = numbers.each { |n| n * 2 } # The block's result is discarded by eachputs "Returned value from 'each': #{returned_value.inspect}" #=> [1, 2, 3, 4, 5]This will produce the following result:
Iterating through numbers:Current number: 1Current number: 2Current number: 3Current number: 4Current number: 5Returned value from 'each': [1, 2, 3, 4, 5]Example with a Hash:
Section titled “Example with a Hash:”#!/usr/bin/env ruby
capitals = { "USA" => "Washington D.C.", "France" => "Paris", "Japan" => "Tokyo" }
capitals.each do |country, capital| puts "The capital of #{country} is #{capital}."end
# For hashes, each_pair is an alias for each.# You can also iterate over just keys or values:capitals.each_key { |country| puts "Country: #{country}" }capitals.each_value { |capital_city| puts "Capital: #{capital_city}" }This will produce:
The capital of USA is Washington D.C.The capital of France is Paris.The capital of Japan is Tokyo.Country: USACountry: FranceCountry: JapanCapital: Washington D.C.Capital: ParisCapital: TokyoThe map (or collect) Iterator:
Section titled “The map (or collect) Iterator:”The map iterator (aliased as collect) transforms a collection. It iterates over each element, executes the block, and builds a new array containing the return values of the block for each element.
Syntax:
Section titled “Syntax:”new_array = collection.map do |variable| # code whose return value will be part of the new arrayend
# Or for single-line blocks:new_array = collection.map { |variable| # expression }Example with an Array:
Section titled “Example with an Array:”#!/usr/bin/env ruby
original_numbers = [1, 2, 3, 4, 5]
# Create a new array with each number squaredsquared_numbers = original_numbers.map do |n| n * nendputs "Original numbers: #{original_numbers.inspect}"puts "Squared numbers: #{squared_numbers.inspect}"
# Using the alias 'collect'uppercased_fruits = ["apple", "banana", "cherry"].collect { |fruit| fruit.upcase }puts "Uppercased fruits: #{uppercased_fruits.inspect}"This will produce:
Original numbers: [1, 2, 3, 4, 5]Squared numbers: [1, 4, 9, 16, 25]Uppercased fruits: ["APPLE", "BANANA", "CHERRY"]Note: While collect is a valid alias, map is generally preferred in modern Ruby code for clarity and consistency.
Example with a Hash:
Section titled “Example with a Hash:”When map is used on a Hash, the block receives [key, value] pairs as arguments. It returns a new array.
#!/usr/bin/env ruby
scores = { "Alice" => 90, "Bob" => 75, "Charlie" => 82 }
descriptions = scores.map do |name, score| "#{name} scored #{score} points."endputs descriptions.inspect
# If you want to transform a hash into another hash, use `transform_values` (Ruby 2.4+) or `map` followed by `to_h`.incremented_scores = scores.transform_values { |score| score + 5 }puts "Incremented scores (Hash): #{incremented_scores.inspect}"
# Using map and to_h for more complex transformations resulting in a Hashstudent_data = scores.map do |name, score| [name.to_sym, { score: score, status: score >= 80 ? "Pass" : "Fail" }]end.to_hputs "Student data (Hash): #{student_data.inspect}"This produces:
["Alice scored 90 points.", "Bob scored 75 points.", "Charlie scored 82 points."]Incremented scores (Hash): {"Alice"=>95, "Bob"=>80, "Charlie"=>87}Student data (Hash): {:Alice=>{:score=>90, :status=>"Pass"}, :Bob=>{:score=>75, :status=>"Fail"}, :Charlie=>{:score=>82, :status=>"Pass"}}Other Useful Iterators from Enumerable:
Section titled “Other Useful Iterators from Enumerable:”select(orfind_all): Returns a new array containing all elements for which the block returns a true value.reject: The opposite ofselect. Returns a new array containing all elements for which the block returns a false or nil value.find(ordetect): Returns the first element for which the block returns a true value.reduce(orinject): Combines all elements of the collection by applying a binary operation, specified by a block or a symbol. It’s powerful for summing numbers, accumulating values, etc.any?,all?,none?: Check if any, all, or no elements satisfy a condition given by a block.
#!/usr/bin/env ruby
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = numbers.select { |n| n.even? }puts "Even numbers: #{even_numbers.inspect}" #=> [2, 4, 6]
sum = numbers.reduce(0) { |accumulator, current_element| accumulator + current_element }# Alternative: numbers.reduce(:+) works for simple sumsputs "Sum of numbers: #{sum}" #=> 21
puts "Are there any numbers > 5? #{numbers.any? { |n| n > 5 }}" #=> truePractical Application:
Section titled “Practical Application:”Iterators are used everywhere in Ruby. For example, processing user input, transforming data from a database or API, filtering lists, and generating reports. Chaining iterators allows for elegant and readable data manipulation pipelines.
data = [{id: 1, name: "Alice", active: true}, {id: 2, name: "Bob", active: false}, {id: 3, name: "Charlie", active: true}]
# Get names of active users, uppercasedactive_user_names = data.select { |user| user[:active] } .map { |user| user[:name].upcase }
puts "Active user names: #{active_user_names.inspect}" #=> ["ALICE", "CHARLIE"]For more information, refer to the Ruby documentation for the Enumerable module: https://ruby-doc.org/core/Enumerable.html