Skip
Arish's avatar

14. Enumerables and Iterators


Enumerables and Iterators

The Enumerable module is one of Ruby's most powerful features. It provides a set of methods for traversing, searching, and manipulating collections. You'll use these constantly in Rails!

The Each Method

The foundation of iteration in Ruby:

ruby
1# Array iteration
2fruits = ["apple", "banana", "cherry"]
3
4fruits.each do |fruit|
5  puts "I like #{fruit}"
6end
7
8# With index
9fruits.each_with_index do |fruit, index|
10  puts "#{index + 1}. #{fruit}"
11end
12
13# Hash iteration
14user = { name: "John", age: 30, city: "NYC" }
15
16user.each do |key, value|
17  puts "#{key}: #{value}"
18end
19
20# Range iteration
21(1..5).each { |n| puts n }

Map/Collect - Transform Elements

Create a new array by transforming each element:

ruby
1numbers = [1, 2, 3, 4, 5]
2
3# Double each number
4doubled = numbers.map { |n| n * 2 }
5# => [2, 4, 6, 8, 10]
6
7# Get user names from objects
8users = [
9  { name: "John", age: 30 },
10  { name: "Jane", age: 25 }
11]
12
13names = users.map { |user| user[:name] }
14# => ["John", "Jane"]
15
16# Shorthand for simple method calls
17words = ["hello", "world"]
18words.map(&:upcase)
19# => ["HELLO", "WORLD"]
20
21# In Rails, you'll often see:
22@users.map(&:email)  # Get all user emails

Select/Find_all - Filter Elements

Get elements that match a condition:

ruby
1numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2
3# Get even numbers
4evens = numbers.select { |n| n.even? }
5# => [2, 4, 6, 8, 10]
6
7# Get numbers greater than 5
8large = numbers.select { |n| n > 5 }
9# => [6, 7, 8, 9, 10]
10
11# Filter users by age
12users = [
13  { name: "John", age: 30 },
14  { name: "Jane", age: 17 },
15  { name: "Bob", age: 25 }
16]
17
18adults = users.select { |user| user[:age] >= 18 }
19# => [{ name: "John", age: 30 }, { name: "Bob", age: 25 }]

Reject - Exclude Elements

The opposite of select:

ruby
1numbers = [1, 2, 3, 4, 5]
2
3# Reject even numbers (keep odd)
4odds = numbers.reject { |n| n.even? }
5# => [1, 3, 5]
6
7# Reject inactive users
8users = users.reject { |user| user[:status] == "inactive" }

Find/Detect - Find First Match

Get the first element that matches:

ruby
1numbers = [1, 2, 3, 4, 5]
2
3# Find first even number
4first_even = numbers.find { |n| n.even? }
5# => 2
6
7# Find user by name
8users = [
9  { name: "John", age: 30 },
10  { name: "Jane", age: 25 }
11]
12
13john = users.find { |user| user[:name] == "John" }
14# => { name: "John", age: 30 }
15
16# Returns nil if not found
17missing = numbers.find { |n| n > 100 }
18# => nil

Reduce/Inject - Accumulate Values

Combine all elements into a single value:

ruby
1numbers = [1, 2, 3, 4, 5]
2
3# Sum all numbers
4sum = numbers.reduce(0) { |total, n| total + n }
5# => 15
6
7# Shorthand
8sum = numbers.reduce(0, :+)
9# => 15
10
11# Without initial value (uses first element)
12sum = numbers.reduce(:+)
13# => 15
14
15# Multiply all numbers
16product = numbers.reduce(1, :*)
17# => 120
18
19# Build a hash
20words = ["apple", "banana", "cherry"]
21word_lengths = words.reduce({}) do |hash, word|
22  hash[word] = word.length
23  hash
24end
25# => { "apple" => 5, "banana" => 6, "cherry" => 6 }
26
27# Flatten nested arrays
28nested = [[1, 2], [3, 4], [5, 6]]
29flat = nested.reduce([], :+)
30# => [1, 2, 3, 4, 5, 6]

All?, Any?, None?, One?

Check conditions across collections:

ruby
1numbers = [2, 4, 6, 8, 10]
2
3# Are all numbers even?
4numbers.all?(&:even?)  # => true
5
6# Is any number greater than 5?
7numbers.any? { |n| n > 5 }  # => true
8
9# Are none of the numbers negative?
10numbers.none?(&:negative?)  # => true
11
12# Is exactly one number equal to 4?
13numbers.one? { |n| n == 4 }  # => true

Count, Size, Length

Get collection size or count matching elements:

ruby
1numbers = [1, 2, 3, 4, 5, 2, 2]
2
3numbers.length  # => 7
4numbers.size    # => 7 (alias)
5numbers.count   # => 7
6
7# Count specific value
8numbers.count(2)  # => 3
9
10# Count with condition
11numbers.count(&:even?)  # => 4

Sort and Sort_by

Order elements:

ruby
1numbers = [3, 1, 4, 1, 5, 9, 2, 6]
2
3numbers.sort      # => [1, 1, 2, 3, 4, 5, 6, 9]
4numbers.sort.reverse  # => [9, 6, 5, 4, 3, 2, 1, 1]
5
6# Custom sort
7words = ["banana", "apple", "cherry"]
8words.sort  # => ["apple", "banana", "cherry"]
9
10# Sort by length
11words.sort_by(&:length)
12# => ["apple", "cherry", "banana"]
13
14# Sort users by age
15users = [
16  { name: "John", age: 30 },
17  { name: "Jane", age: 25 },
18  { name: "Bob", age: 35 }
19]
20
21users.sort_by { |user| user[:age] }
22# => [Jane, John, Bob]
23
24# Descending order
25users.sort_by { |user| -user[:age] }
26# => [Bob, John, Jane]

Group_by - Group Elements

Create a hash of grouped elements:

ruby
1numbers = [1, 2, 3, 4, 5, 6]
2
3# Group by even/odd
4grouped = numbers.group_by(&:even?)
5# => { false => [1, 3, 5], true => [2, 4, 6] }
6
7# Group users by role
8users = [
9  { name: "John", role: "admin" },
10  { name: "Jane", role: "user" },
11  { name: "Bob", role: "admin" }
12]
13
14by_role = users.group_by { |user| user[:role] }
15# => { "admin" => [...], "user" => [...] }

Partition - Split into Two

Divide elements based on condition:

ruby
1numbers = [1, 2, 3, 4, 5, 6]
2
3evens, odds = numbers.partition(&:even?)
4# evens => [2, 4, 6]
5# odds => [1, 3, 5]

First, Last, Take, Drop

Access specific elements:

ruby
1numbers = [1, 2, 3, 4, 5]
2
3numbers.first     # => 1
4numbers.first(2)  # => [1, 2]
5numbers.last      # => 5
6numbers.last(2)   # => [4, 5]
7
8numbers.take(3)   # => [1, 2, 3]
9numbers.drop(2)   # => [3, 4, 5]

Compact - Remove Nils

ruby
1array = [1, nil, 2, nil, 3]
2
3array.compact  # => [1, 2, 3]

Flatten - Flatten Nested Arrays

ruby
1nested = [[1, 2], [3, [4, 5]]]
2
3nested.flatten      # => [1, 2, 3, 4, 5]
4nested.flatten(1)   # => [1, 2, 3, [4, 5]] (one level)

Uniq - Remove Duplicates

ruby
1numbers = [1, 2, 2, 3, 3, 3]
2
3numbers.uniq  # => [1, 2, 3]
4
5# Unique by attribute
6users.uniq { |user| user[:email] }

Chaining Methods

You can chain enumerable methods:

ruby
1numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2
3result = numbers
4  .select(&:even?)        # Get even numbers
5  .map { |n| n * 2 }      # Double them
6  .take(3)                # Take first 3
7# => [4, 8, 12]
8
9# In Rails
10@users
11  .where(active: true)
12  .order(:name)
13  .map(&:email)

Mastering enumerables will make you incredibly productive in Ruby and Rails!