How to Merge Ruby Hashes

How to merge Ruby hashes

There is a Hash#merge method:

ruby-1.9.2 > a = {:car => {:color => "red"}}
=> {:car=>{:color=>"red"}}
ruby-1.9.2 > b = {:car => {:speed => "100mph"}}
=> {:car=>{:speed=>"100mph"}}
ruby-1.9.2 > a.merge(b) {|key, a_val, b_val| a_val.merge b_val }
=> {:car=>{:color=>"red", :speed=>"100mph"}}

You can create a recursive method if you need to merge nested hashes:

def merge_recursively(a, b)
a.merge(b) {|key, a_item, b_item| merge_recursively(a_item, b_item) }
end

ruby-1.9.2 > merge_recursively(a,b)
=> {:car=>{:color=>"red", :speed=>"100mph"}}

How to merge multiple hashes in Ruby?

You could do it like this:

h, h2, h3  = { a: 1 }, { b: 2 }, { c: 3 }
a = [h, h2, h3]

p Hash[*a.map(&:to_a).flatten] #= > {:a=>1, :b=>2, :c=>3}

Edit: This is probably the correct way to do it if you have many hashes:

a.inject{|tot, new| tot.merge(new)}
# or just
a.inject(&:merge)

How to merge two hashes with same ID by combining keys with different values

hashes.
group_by { |e| [e[:id], e[:value]] }.
map { |_, g|
g.first.clone.
tap { |t|
t[:source] = g.reduce([]) { |a, e| a << e[:source] }
}
}

First group the hashes by the part that should be the same. We don't care about the key any more; but each group itself will map to something very similar to the first element of the group. Clone it so the original hashes elements are not mutated; then replace its :source with the accumulation of all the group's elements' :source values.

How to correctly merge one hash into another, replacing the first key. RoR

In Rails #attributes returns a hash with string keys:

irb(main):001:0> note = Notification.new(title: 'All your base are belong to us', body: 'Loren Ipsum...')
irb(main):002:0> note.attributes
=> {"id"=>nil, "title"=>"All your base are belong to us", "body"=>"Loren Ipsum...", "read_at"=>nil, "created_at"=>nil, "updated_at"=>nil}

If you want to replace a key in the hash you either need to use a hash with string keys:

irb(main):003:0> note.attributes.merge("body" => "Moahahahahahaha")
=> {"id"=>nil, "title"=>"All your base are belong to us", "body"=>"Moahahahahahaha", "read_at"=>nil, "created_at"=>nil, "updated_at"=>nil}

Or you need to change the keys of the hash to symbols which can be done with Hash#symbolize_keys:

irb(main):004:0> note.attributes.symbolize_keys.merge(body: "Moahahahahahaha")
=> {:id=>nil, :title=>"All your base are belong to us", :body=>"Moahahahahahaha", :read_at=>nil, :created_at=>nil, :updated_at=>nil}

This is a pretty common source of errors for new developers as Rails abstracts away the difference between symbol and string keys in many places through the use of ActiveSupport::HashWithIndifferentAccess or hash like objects like ActionController::Parameters that have indifferent access while Ruby itself is strict about the difference.

irb(main):008:0> { "foo" => "bar" }.merge(foo: 'baz')
=> {"foo"=>"bar", :foo=>"baz"}
irb(main):009:0> { "foo" => "bar" }.with_indifferent_access.merge(foo: 'baz')
=> {"foo"=>"baz"}

If you ever need to do this with a nested hash you can use the recursive versions deep_symbolize_keys and deep_merge.

How to merge two arrays of hashes

uniq would work if you concatenate the arrays in reverse order:

(b + a).uniq { |h| h[:key] }
#=> [
# {:key=>1, :value=>"bar"},
# {:key=>1000, :value=>"something"},
# {:key=>2, :value=>"baz"}
# ]

It doesn't however preserve the order.

ruby hash merge with a block

The first merge works as described in the documentation.

The block is invoked only to solve conflicts, when a key is present in both hashes. On the first call to Hash#merge!, a is empty, hence no conflict occurred and the content of b is copied into a without any changes.

You can fix the code by initializing a with {x: 0, y: 0}.

Merge Ruby Hash values with same key

I can't see a simpler version of your code. To make it fully work, you can use the first argument in the merge block instead of dismissing it to differentiate when you need to merge a and b or when you just use a. Your line becomes:

s.merge(l) { |key, a, b| key == :a ? a : [a, b].uniq.join(", ") }

Merge multiple hashes after new key

Using merge! is fine and well understood. However, as you are setting a single key on your base_options hash, you can also simple use the hash accessor, i.e.

base_options[:checks] = document_check.merge!(identity_check, dummy_check)

Note that this will also change the document_hash object as merge! modified the receiver. If this is not desired, you can also use merge and return a new Hash. Thus could look like:

base_options[:checks] = document_check.merge(identity_check, dummy_check)

or equivalently

base_options[:checks] = {}.merge!(document_check, identity_check, dummy_check)

The latter option is slightly slower but might better show your intended behavior and is thus easier to understand to readers of your code.

Merge an array of hashes by key-value pair

The more-or-less generic and extendable variant would be:

input.
group_by { |h| h['abc_id'] }.
map do |k, v|
v.reduce do |acc, arr|
# use `+` instead of `|` to save duplicates ⇓⇓⇓
acc.merge(arr) { |_, v1, v2| Array === v1 ? v1 | v2 : v1 }
end
end
#⇒ [{"abc_id"=>"1234", "def_id"=>["33", "44"]},
# {"abc_id"=>"5678", "def_id"=>["11", "22", "55", "66"]}]


Related Topics



Leave a reply



Submit