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
Rhc Setup Gives Error 'No Such File Dl/Import'
How to Get a Specific Output Iterating a Hash in Ruby
How to Count Duplicate Elements in a Ruby Array
Incompatible Character Encodings: Ascii-8Bit and Utf-8
Custom Authentication Strategy For Devise
Access Variables Programmatically by Name in Ruby
&:Views_Count' in 'Post.Published.Collect(&:Views_Count)'
Usage of Attr_Accessor in Rails
Altering the Primary Key in Rails to Be a String
Block Definition - Difference Between Braces and Do-End
Difference Between Datetime and Time in Ruby
Nokogiri Installation Fails -Libxml2 Is Missing
How to Extract Url Parameters from a Url With Ruby or Rails