Merging and Transforming
Part of the Logic & Flow section of Coddy's Ruby journey — lesson 36 of 56.
Two operations come up so often when working with hashes that Ruby gives them their own methods.
merge combines two hashes into a new one. When both have the same key, the right-hand value wins:
defaults = { color: "red", size: "M" }
custom = { size: "L" }
settings = defaults.merge(custom)
puts settings.inspect # {color: "red", size: "L"}Neither original is modified, merge returns a new hash.
transform_values builds a new hash with the same keys but each value passed through a block:
prices = { apple: 1, bread: 3 }
with_tax = prices.transform_values { |p| p * 1.2 }
puts with_tax.inspect # {apple: 1.2, bread: 3.6}And transform_keys does the same for keys, useful when converting strings to symbols, or normalizing case:
{ "a" => 1, "B" => 2 }.transform_keys(&:upcase)
# {"A"=>1, "B"=>2}Challenge
MediumTwo cart hashes are given: monday and tuesday, each maps a string item name to an integer quantity. The shop wants a normalized two-day total.
Produce one final hash by:
merge-ing the two hashes with a block so that keys present in both have their quantities summed (not overwritten)transform_keys(&:to_sym)on the result, so keys become symbolstransform_valuesto apply a 10% bulk discount: every quantity becomes(qty * 9 / 10)using integer math
Print the final hash with inspect.
For the default data, the output is:
{:apples=>9, :bread=>4, :milk=>5, :cheese=>1}(Apples: 5 + 5 = 10, then 9. Bread: 3 + 2 = 5, then 4. Milk: 6 only on Monday → 5. Cheese: 2 only on Tuesday → 1.)
Cheat sheet
merge combines two hashes; when keys conflict, the right-hand value wins:
defaults = { color: "red", size: "M" }
custom = { size: "L" }
settings = defaults.merge(custom)
# {color: "red", size: "L"}Pass a block to merge to handle duplicate keys manually:
a.merge(b) { |key, old_val, new_val| old_val + new_val }transform_values returns a new hash with each value passed through a block:
prices = { apple: 1, bread: 3 }
prices.transform_values { |p| p * 1.2 }
# {apple: 1.2, bread: 3.6}transform_keys does the same for keys:
{ "a" => 1 }.transform_keys(&:upcase) # {"A"=>1}
{ "a" => 1 }.transform_keys(&:to_sym) # {a: 1}Try it yourself
monday = { "apples" => 5, "bread" => 3, "milk" => 6 }
tuesday = { "apples" => 5, "bread" => 2, "cheese" => 2 }
# TODO: merge-with-block to sum overlapping keys, then symbolize keys,
# then apply * 9 / 10 to every value, then inspect
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Logic & Flow
1Strings In Depth
String Methods OverviewString InterpolationIterating Over StringsSplit and JoinRecap - String Weaver4Blocks, Procs & Lambdas
What is a Block?do..end vs BracesThe yield KeywordBlock ParametersProcs and LambdasRecap - Custom Iterator7Hashes Part 2
Hash.new with DefaultsIterating HashesNested HashesMerging and TransformingRecap - Frequency Counter10Project - Student Records
Project OverviewAdd Student5Enumerable Powerhouse
Select and RejectChaining MapReduce / Injectcount, all?, any?, none?group_by and partitionsort_by, min_by, max_byRecap - Data Pipeline8Advanced Decision Making
Case with Classes & RegexMulti-value whenTernary OperatorInline if / unlessRecap - Grade Classifier