Created
December 19, 2010 17:31
-
-
Save arnab/747505 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# = Hash Recursive Merge | |
# | |
# Merges a Ruby Hash recursively, Also known as deep merge. | |
# Recursive version of Hash#merge and Hash#merge!. | |
# | |
# Category:: Ruby | |
# Package:: Hash | |
# Author:: Simone Carletti <weppos@weppos.net> | |
# Copyright:: 2007-2008 The Authors | |
# License:: MIT License | |
# Link:: http://www.simonecarletti.com/ | |
# Source:: http://gist.github.com/gists/6391/ | |
# | |
module HashRecursiveMerge | |
# | |
# Recursive version of Hash#merge! | |
# | |
# Adds the contents of +other_hash+ to +hsh+, | |
# merging entries in +hsh+ with duplicate keys with those from +other_hash+. | |
# | |
# Compared with Hash#merge!, this method supports nested hashes. | |
# When both +hsh+ and +other_hash+ contains an entry with the same key, | |
# it merges and returns the values from both arrays. | |
# | |
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}} | |
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}} | |
# h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}} | |
# | |
# Simply using Hash#merge! would return | |
# | |
# h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}} | |
# | |
def rmerge!(other_hash) | |
merge!(other_hash) do |key, oldval, newval| | |
oldval.class == self.class ? oldval.rmerge!(newval) : newval | |
end | |
end | |
# | |
# Recursive version of Hash#merge | |
# | |
# Compared with Hash#merge!, this method supports nested hashes. | |
# When both +hsh+ and +other_hash+ contains an entry with the same key, | |
# it merges and returns the values from both arrays. | |
# | |
# Compared with Hash#merge, this method provides a different approch | |
# for merging nasted hashes. | |
# If the value of a given key is an Hash and both +other_hash+ abd +hsh | |
# includes the same key, the value is merged instead replaced with | |
# +other_hash+ value. | |
# | |
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}} | |
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}} | |
# h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}} | |
# | |
# Simply using Hash#merge would return | |
# | |
# h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}} | |
# | |
def rmerge(other_hash) | |
r = {} | |
merge(other_hash) do |key, oldval, newval| | |
r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval | |
end | |
end | |
end | |
class Hash | |
include HashRecursiveMerge | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "spec_helper" | |
describe Hash do | |
before(:each) do | |
@simple_h1 = { 'a' => 100, 'b' => 200 } | |
@simple_h2 = { 'b' => 254, 'c' => 300 } | |
@nested_h1 = { 'a' => 100, 'b' => 200, 'c' => { 'c1' => 12, 'c2' => 14 }} | |
@nested_h2 = { 'b' => 254, 'c' => { 'c1' => 16, 'c3' => 94 }} | |
end | |
describe "#rmerge" do | |
it "should merge simple hashes" do | |
@simple_h1.rmerge(@simple_h2).should == { 'a' => 100, 'b' => 254, 'c' => 300 } | |
@simple_h2.rmerge(@simple_h1).should == { 'a' => 100, 'b' => 200, 'c' => 300 } | |
end | |
it "should merge nested hashes" do | |
@nested_h1.rmerge(@nested_h2).should == | |
{ 'a' => 100, 'b' => 254, 'c' => { 'c1' => 16, 'c2' => 14, 'c3' => 94 }} | |
@nested_h2.rmerge(@nested_h1).should == | |
{ 'a' => 100, 'b' => 200, 'c' => { 'c1' => 12, 'c2' => 14, 'c3' => 94 }} | |
end | |
end | |
describe "#rmerge!" do | |
it "should merge simple hashes and modify the receiver" do | |
@simple_h1.rmerge!(@simple_h2) | |
@simple_h1.should == { 'a' => 100, 'b' => 254, 'c' => 300 } | |
@simple_h1 = { 'a' => 100, 'b' => 200 } | |
@simple_h2.rmerge!(@simple_h1) | |
@simple_h2.should == { 'a' => 100, 'b' => 200, 'c' => 300 } | |
end | |
it "should merge nested hashes and modify the receiver" do | |
@nested_h1.rmerge!(@nested_h2) | |
@nested_h1.should == | |
{ 'a' => 100, 'b' => 254, 'c' => { 'c1' => 16, 'c2' => 14, 'c3' => 94 }} | |
@nested_h1 = { 'a' => 100, 'b' => 200, 'c' => { 'c1' => 12, 'c2' => 14 }} | |
@nested_h2.rmerge!(@nested_h1) | |
@nested_h2.should == | |
{ 'a' => 100, 'b' => 200, 'c' => { "c1" => 12, "c2" => 14, "c3" => 94 }} | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'test/unit' | |
require 'hash_recursive_merge' | |
class HashRecursiveMergeTest < Test::Unit::TestCase | |
def test_rmerge_should_be_available_to_any_instance_of_hash | |
assert(Hash.new.public_methods.include?('rmerge')) | |
end | |
def test_rmerge_bang_should_be_available_to_any_instance_of_hash | |
assert(Hash.new.public_methods.include?('rmerge!')) | |
end | |
def test_rmerge_should_merge_simple_hashes | |
h1 = { 'a' => 100, 'b' => 200 } | |
h2 = { 'b' => 254, 'c' => 300 } | |
assert_equal({ 'a' => 100, 'b' => 254, 'c' => 300 }, h1.rmerge(h2)) | |
assert_equal({ 'a' => 100, 'b' => 200, 'c' => 300 }, h2.rmerge(h1)) | |
end | |
def test_rmerge_should_merge_nested_hashes | |
h1 = { 'a' => 100, 'b' => 200, "c" => { "c1" => 12, "c2" => 14 }} | |
h2 = { 'b' => 254, "c" => { "c1" => 16, "c3" => 94 }} | |
assert_equal({ 'a' => 100, 'b' => 254, 'c' => { "c1" => 16, "c2" => 14, "c3" => 94 }}, h1.rmerge(h2)) | |
assert_equal({ 'a' => 100, 'b' => 200, 'c' => { "c1" => 12, "c2" => 14, "c3" => 94 }}, h2.rmerge(h1)) | |
end | |
def test_rmerge_bang_should_merge_simple_hashes | |
h1 = { 'a' => 100, 'b' => 200 } | |
h2 = { 'b' => 254, 'c' => 300 } | |
h1.rmerge!(h2) | |
assert_equal({ 'a' => 100, 'b' => 254, 'c' => 300 }, h1) | |
h1 = { 'a' => 100, 'b' => 200 } | |
h2 = { 'b' => 254, 'c' => 300 } | |
h2.rmerge!(h1) | |
assert_equal({ 'a' => 100, 'b' => 200, 'c' => 300 }, h2) | |
end | |
def test_rmerge_should_merge_nested_hashes | |
h1 = { 'a' => 100, 'b' => 200, "c" => { "c1" => 12, "c2" => 14 }} | |
h2 = { 'b' => 254, "c" => { "c1" => 16, "c3" => 94 }} | |
h1.rmerge!(h2) | |
assert_equal({ 'a' => 100, 'b' => 254, 'c' => { "c1" => 16, "c2" => 14, "c3" => 94 }}, h1) | |
h1 = { 'a' => 100, 'b' => 200, "c" => { "c1" => 12, "c2" => 14 }} | |
h2 = { 'b' => 254, "c" => { "c1" => 16, "c3" => 94 }} | |
h2.rmerge!(h1) | |
assert_equal({ 'a' => 100, 'b' => 200, 'c' => { "c1" => 12, "c2" => 14, "c3" => 94 }}, h2) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment