2

I am trying to merge two Hashes.I got the code from here. As you can see I want to add one more hobby to my list. There are many hobbies. When h1 is formed ,only one hobby was available. When the second hobby arrived I wanted to merge it with the previous hash.

The structure of the Hashes are :

h1 = {"students"=>[{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling"}]}]}

h2 = {"students"=>[{"name"=>"bobby", "hobbies"=>[{"indoor"=>"reading"}]}]}

I ran this code:

res = h1.merge(h2) {|key,val1,val2| val1.merge val2}

The error I got was:

 undefined method `merge' for [{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling"}]}]:Array (NoMethodError)

So since array is involved, I don't know what to do. Any Help.

Required Output:

{"students"=>[{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling", "indoor"=>"reading"}]}]}
5
  • "pretty clear"? No, the hash would have been pretty clear if you wrote h1 = {"students"=>[{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling"}]}]}. Also, we don't know what you want to do. What do you want to happen when there are arrays present? Give the output example as well as input examples. Commented Mar 6, 2015 at 6:20
  • What’s the reason of storing hobbies as an array consisting of one hash element? Commented Mar 6, 2015 at 6:55
  • @mudasobwa. There are many hobbies. When h1 is formed ,only one hobby was available. When the second hobby arrived I wanted to merge it with the previous hash. Commented Mar 6, 2015 at 6:59
  • That explains why you do have hash there. It’s still unclear, why you decided to wrap hobbies hash in array. Commented Mar 6, 2015 at 7:01
  • 1
    I just updated my answer to be working with many students. Commented Mar 6, 2015 at 7:07

2 Answers 2

4

The built-in marge method cannot do what you want. You should write the original code to merge these structure like this.

def recursive_merge(h1, h2)
   case h1
   when Hash
      (h1.keys + h2.keys).each do |key|
         if h1.include?(key) && h2.include?(key) then
            recursive_merge(h1[key], h2[key])
         elsif h2.include?(key)
            h1[key] = h2[key]
         end
      end
   when Array
      for i in 0...([h1.count, h2.count].max)
         if i < h1.count && i < h2.count then
            recursive_merge(h1[i], h2[i])
         elsif i < h2.count then
            h1[i] = h2[i]
         end
      end
   end
end

recursive_merge(h1, h2)
puts h1

This method results the follwing output. Please note that this method overwrites h1 itself.

{"students"=>[{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling", "indoor"=>"reading"}]}]}
Sign up to request clarification or add additional context in comments.

Comments

2

The answer on your question might be:

h1.merge(h2) { |k, v1, v2| 
  v1.map.with_index { |v1e, idx| 
    v1e['hobbies'].first.merge! v2[idx]['hobbies'].first
    v1e  
  }
  v1 
}

#⇒ {
#  "students" => [
#    [0] {
#      "hobbies" => [
#        [0] {
#           "indoor" => "reading",
#          "outdoor" => "cycling"
#        }
#      ],
#         "name" => "bobby"
#    }
#  ]
# }

But I would definitely redesign hobbies to be a hash, not an array of hashes, eliminating weird first calls:

v1e['hobbies'].merge! v2[idx]['hobbies']

Hope it helps.

2 Comments

Reality check , the actual Hash is very different and has technical names. For the sake of understanding, i tried to give it meaningful names. This works! But I can't understand what you wanted those variables to mean - |vle,idx|
v1, v2 stay for value1, value2. v1e stays for value1_element. idx is apparetnly an index.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.