Map, Reduce & Select in Ruby
Complex Solutions # MapReduceSelectSeries — Episode 1
👉Click here to view the full interactive notebook for this episode.
This Episode covers complex problems using map, reduce, and select, starting with debugging to help you better understand these concepts.
Index:
Debugging Blocks for Map, Reduce, and Select
Debugging Map Blocks
Debugging Reduce Blocks
Debugging Select Blocks
FizzBuzz
Sum of Odd Cubes
Sort a List of Names by Surname
Convert a List of Names to CSVLets get Started!
Note: if you get stuck, please see my repo. Or as Jupyter Notebook.
0.Debugging — Map Blocks: Remember, map processes each element of the collection with every block run, so debugging helps you see how the block affects each element.
[1,2,3,4,5].map do |n|
result = n * n
puts "The result of function on element: #{n} is #{result}"
result
endThe result of function on element: 1 is 1
The result of function on element: 2 is 4
The result of function on element: 3 is 9
The result of function on element: 4 is 16
The result of function on element: 5 is 251. Debugging — Reduce Blocks: Debugging reduce is a bit different because the memo is carried from element to element. Therefore, you want to print out the element as well as the memo.
final_anwer = [1,2,3,4,5].reduce(0) do |memo, n|
memo = memo + n
puts "After operating on number: #{n}, the memo is #{memo.inspect}"
memo
end After operating on number: 1, the memo is 1
After operating on number: 2, the memo is 3
After operating on number: 3, the memo is 6
After operating on number: 4, the memo is 10
After operating on number: 5, the memo is 15
=> 152. Debugging — Select Blocks:Debugging select is easy because the only thing you need to know is whether the predicate was true for a particular element.
For example, a select filter that only keeps even num:
[1,2,3,4,5].select do |n|
predicate = n %2 == 0
puts "The predicate is #{predicate} for number #{n}"
predicate
endThe predicate is false for number 1
The predicate is true for number 2
The predicate is false for number 3
The predicate is true for number 4
The predicate is false for number 5
=> [2, 4]3. FizzBuzz : This program is used as a programming question. Write a program to go through a list of integers 1..N. If the number is divisible by 2, print “fizz”; if it is divisible by 3, print “buzz”; if it is divisible by 2 and 3, print “fizzbuzz”; otherwise, print the number.
fizzbuzz_list =
(1..15).map do |n|
if n % 2 == 0
"fizz"
elsif n % 3 == 0
"buzz"
elsif n % 6 == 0
"fizzbuzz"
else
n.to_s
end
end=> ["1", "fizz", "buzz", "fizz", "5", "fizz", "7", "fizz", "buzz", "fizz", "11", "fizz", "13", "fizz", "buzz"]That’s close, but the fizzbuzz examples didn’t happen at 6 and 12. A closer
inspection of the code suggests that if the number is divisible by 6, it will
always be even and will be caught by the n%2==0 clause. However, if we
catch the n%6==0 clause first, what happens?
fizzbuzz_list =
(1..15).map do |n|
if n%6 == 0
"fizzbuzz"
elsif n%3 ==0
"buzz"
elsif n%2 == 0
"fizz"
else
n.to_s
end
end=> ["1", "fizz", "buzz", "fizz", "5", "fizzbuzz", "7", "fizz", "buzz", "fizz", "11", "fizzbuzz", "13", "fizz", "buzz"]4.Sum of Odd Cubes: To sum the cubes of the odd numbers from 1 to 1000, start by filtering the odd numbers (those where number % 2 == 1). Then, cube each of those numbers and sum the results. For example, for odd numbers under 10, the sum of their cubes is 1³ + 3³ + 5³ + 7³ + 9³ = 1225.
Solved:
(1..1000).select do |n|
n % 2 == 1
end.map do |odd_n|
odd_n ** 3
end.reduce(0) do |previous_sum, odd_cube|
previous_sum + odd_cube
end=> 124999750000This code filters the odd numbers from 1 to 1000, cubes each odd number, and then sums them up:
(1..1000).select { |n| n % 2 == 1 }filters the odd numbers..map { |odd_n| odd_n ** 3 }cubes each odd number..reduce(0) { |previous_sum, odd_cube| previous_sum + odd_cube }sums the cubes.
The result is the sum of the cubes of all odd numbers from 1 to 1000.
5.Sort a List of Names by Surname: Suppose you have a list of names in a simple text file called names.txt.
Get J3’s stories in your inbox
Join Medium for free to get updates from this writer.
The challenge is to get the full names printed in order of the surname:
John Smith
Azamat Bagatov
Hafaz Aladeen
Ramachandran Balasubrahmaniam
Ping Li
Wilfredo Caguiat
Eriks Ivanans
Canaan Banana
Ion Iliescu
Werner Klempner
Thierry Giscard
Joao SoaresHere is a step by step:
Read file → split into names → extract last names → sort by last name → reconstruct full names.names = File.open("05_names.txt", "r").read #read the file into a string
namelist = names.split("\n") #split the string into an array of names
namelist.map { |fullname| fullname.split(" ") } #split each name into an array of first and last names
namelist.map { |fullname| fullname.split(" ")[1] } #extract only the last names
namelist.map { |fullname| fullname.split(" ")[1] }.sort #sort by last name
namelist.map { |fullname| [fullname.split(" ")[1], fullname] } #annotate names with their last names
sorted_list = namelist.map { |fullname| [fullname.split(" ")[1], fullname] }.sort_by { |last_name, _| last_name } #sort by last name
sorted_names = sorted_list.map { |_, fullname| fullname }.join("\n") #strip sorting keys and reconstruct full namesPrint the final sorted names:
puts sorted_namesHafaz Aladeen
Azamat Bagatov
Ramachandran Balasubrahmaniam
Canaan Banana
Wilfredo Caguiat
Thierry Giscard
Ion Iliescu
Eriks Ivanans
Werner Klempner
Ping Li
John Smith
Joao SoaresIn one line:
File.open("05_names.txt", "r").read.split("\n").map {|fullname| [fullname.split(" ")[1],fullname]}.sort_by {|last_name, _| last_name }.map{|_, fullname| fullname}.join("\n")First Solution:
namelist = File.open("05_names.txt", "r").read.split("\n") # Read names from file
namelist
.map { |fullname| [fullname.split(" ")[1] || fullname, fullname] } # Extract last name, fallback to full name
.sort_by { |last_name, _| last_name } # Sort by last name
.map { |_, fullname| fullname } # Keep only the full name
.join("\n") # Join names into a string
Second Solution:
File.open("05_names.txt", "r").read.split("\n")
.map { |fullname| fullname.split(" ", 2) } # Split into first and last name
.map { |first, last| [last || first, first, last].compact } # Ensure last name exists
.sort_by { |last, _, _| last } # Sort by last name
.map { |_, first, last| [first, last].compact.join(" ") } # Reconstruct full names
.join("\n") # Join names into a single stringKey Differences:
✅ Both solutions start by reading from a file.
✅ The first solution keeps it simple by assuming names are already in a list.
✅ The second solution is slightly more robust, handling cases where names might have multiple words.
Both will now correctly sort names by last name while keeping the full names intact. 🚀
6. One typical problem is to get a text file that you need to be able to import into a CSV file
For example, to convert 05_names.txt into 06_names.csv, you can use the following:
File.open("05_names.txt", "r").read
File.open("05_names.txt", "r").read.split("\n")
File.open("05_names.txt", "r").read.split("\n").map { |name| name.split(" ") }
File.open("05_names.txt", "r").read.split("\n").map { |name| name.split(" ").reverse.join(", ") }
File.open("05_names.txt", "r").read.split("\n").map { |name| name.split(" ").reverse.join(", ") }.join("\n")
File.write("06_names.csv", ["Surname, First Name", *File.read("05_names.txt").split("\n").map { |name| name.split(" ", 2).reverse.join(", ") }].join("\n"))This will save the 06_names.csv file in the current working directory.
Explanation:
File.open("05_names.txt", "r").read.split("\n")→ Reads05_names.txtand splits it into an array of names..map { |name| name.split(" ").reverse.join(", ") }→ Splits each name into first and last, then reverses and joins them as"Last, First".(["Surname, First Name"] + ...)→ Adds a header row..join("\n")→ Joins everything into a string with newlines.File.write("06_names.csv", ...)→ Writes the final output to06_names.csv.
Now, 06_names.csv will contain the correctly formatted names.
In this episode, we explored how to approach and solve complex problems using Ruby’s map, reduce, and select methods. We started with debugging techniques for these methods, followed by practical examples like FizzBuzz, summing the cubes of odd numbers, sorting names by surname, and converting text files to CSV format. Each of these problems provided a hands-on way to deepen your understanding of these powerful array manipulation techniques.
In the next episode, we’ll dive into more advanced challenges and further explore the capabilities of Ruby’s enumerable methods. Stay tuned!
Credits & references
Book: Ruby Data Processing — Using Map, Reduce, and Select by Jay Godse









