Skip to Content
Course content

58: The Enumerable Module Methods: map, select, reduce

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Wait, why use map when I can just use each and a new array?

This is the first thing almost every Rubyist asks. You're thinking: "I can just initialize an empty array, loop through my collection with each, and push the transformed values into that array." You're not wrong—it works. But it's verbose and, frankly, not the "Ruby way."

map (also known as collect) is designed specifically for transformation. It says: "Give me this list, and I'll give you a brand new list of the exact same size, but with every element changed according to this block."

orders = [
  { id: 1, total: 25.50 },
  { id: 2, total: 10.00 },
  { id: 3, total: 42.25 }
]

# The "clunky" way
totals = []
orders.each { |o| totals << o[:total] }

# The map way
totals = orders.map { |o| o[:total] }
# => [25.50, 10.00, 42.25]

I always prefer map because it expresses intent. When I see map in a code review, I immediately know the goal is to create a translated version of the original list. When I see each, I assume you're performing a side effect, like saving to a database or printing to a console.

Is select just a filter? And how does it differ from find?

Exactly. select is your go-to for filtering. It returns a new array containing every element for which the block returns true. If nothing matches, you get an empty array.

The confusion usually happens between select and find (or detect). The difference is simple: select gives you everything that matches; find gives you the first thing that matches and then stops looking.

products = [
  { name: "Hammer", category: "Tools", price: 15 },
  { name: "Screwdriver", category: "Tools", price: 8 },
  { name: "Paintbrush", category: "Painting", price: 12 },
  { name: "Roller", category: "Painting", price: 20 }
]

# I want all the tools
tools = products.select { |p| p[:category] == "Tools" }
# => [{name: "Hammer"...}, {name: "Screwdriver"...}]

# I just want the first painting supply I can find
first_paint = products.find { |p| p[:category] == "Painting" }
# => { name: "Paintbrush", category: "Painting", price: 12 }

Pro tip: If you want the opposite of select (everything that doesn't match), Ruby gives you reject. It's much cleaner than writing select { |p| !p[:category] == "Tools" }.

I'm stuck on reduce. What is the "accumulator" actually doing?

reduce (also called inject) is the most powerful of the three, but it's definitely the hardest to wrap your head around. Think of it as "folding" a list down into a single value. Whether that value is a number, a string, or even a hash, it's up to you.

The "accumulator" (usually named sum or memo) is just a variable that carries the result from one iteration to the next. If you provide an initial value, the accumulator starts there. If not, it starts with the first element of the collection.

cart_items = [12.99, 5.00, 22.50, 3.10]

# We start the accumulator at 0.0
grand_total = cart_items.reduce(0.0) do |sum, price|
  sum + price
end
# => 43.59

Here is the play-by-play of what's happening inside that block:

  • Iteration 1: sum is 0.0, price is 12.99. Returns 12.99.
  • Iteration 2: sum is now 12.99, price is 5.00. Returns 17.99.
  • Iteration 3: sum is 17.99, price is 22.50. Returns 40.49.
  • Iteration 4: sum is 40.49, price is 3.10. Returns 43.59.

Once you're comfortable with this, you'll see a shorthand version in professional code: cart_items.reduce(0, :+). It does the exact same thing as the block above, just way more concisely. I suggest mastering the block version first so you actually understand the mechanics.




📋 Practical Task

Exercise: Inventory Value Calculator

You are building a small inventory management tool for a hardware store. You have an array of hashes representing products in the warehouse. Each product has a name, a price, and a quantity.

inventory = [
  { name: "Power Drill", price: 89.99, quantity: 5 },
  { name: "Box of Nails", price: 12.50, quantity: 20 },
  { name: "Work Bench", price: 150.00, quantity: 2 },
  { name: "Safety Goggles", price: 15.00, quantity: 10 },
  { name: "Tape Measure", price: 8.00, quantity: 15 }
]

Your task is to write a script that does the following:

  1. Use select to create a new list called expensive_items containing only products with a price greater than $20.00.
  2. Use map to create a new list called stock_values that contains the total value for each item in the original inventory (price multiplied by quantity).
  3. Use reduce to calculate the total_warehouse_value by summing up all the values in the stock_values array.

Print the expensive_items, the stock_values, and the total_warehouse_value to the console to verify your results.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.