Merge with block
Ruby has these nice moments when you think i like this method but i need to change how it operates and it turns out that that method is already prepared to receive a block to do what you had in mind.
I had a series of prices coming from a source in the following format:
{"5" => 200, "6" => 600, ...}
, where the key would be the ID of the object and the value the price.
I had multiple series like those, and I had to produce a hash with the averages for every object according to their occurences. Something like this:
old_hash = { "5" => 200, "6" => 300}
new_hash = { "5" => 400, "7" => 100}
old_hash.super_merge(new_hash)
# => {"5"=>300.0, "6"=>300, "7"=>100}
Turns out that .merge
accepts a block in which you can control how the combination is performed:
old_hash = { "5" => 200, "6" => 300}
new_hash = { "5" => 400, "7" => 100}
old_hash.merge(new_hash){|key, v1, v2| (v1 + v2)/2.0}
# => {"5"=>300.0, "6"=>300, "7"=>100}
This is pretty neat. I wanted to see how would I do it on my own and give another go to refinements:
module HashSuperMerge
refine Hash do
def super_merge(other_hash, &block)
new_hash = self.dup
other_hash.each do |key, value|
new_hash[key] = if self.has_key?(key) && block_given?
block.call(key, self[key], other_hash[key])
else
value
end
end
new_hash
end
end
end
# test
require "test/unit"
class TestSimpleNumber < Test::Unit::TestCase
using HashSuperMerge
def setup
@old_hash = { "5" => 200, "6" => 300 }
@new_hash = { "5" => 400, "7" => 100 }
end
def test_without_block
merged_hash = @old_hash.super_merge(@new_hash)
assert_equal 400, merged_hash["5"]
assert_equal 300, merged_hash["6"]
assert_equal 100, merged_hash["7"]
end
def test_with_block
merged_hash = @old_hash.super_merge(@new_hash){|key, v1, v2| v1 + v2}
assert_equal 600, merged_hash["5"]
assert_equal 300, merged_hash["6"]
assert_equal 100, merged_hash["7"]
end
end
Alternatively, you can extend each instance of the hash, removing the refine Hash
block of the module definition and doing a old_hash.extend(HashSuperMerge)
on every iteration.