How to merge two hashes that have same keys in ruby
Use Hash#merge
or Hash#merge!
:
a = {a: 1, b: 2, c: 3}
b = {a: 2, c: 4, b: 3}
a.merge!(b) { |k, o, n| o + n }
a # => {:a=>3, :b=>5, :c=>7}
The block is called with key, old value, new value. And the return value of the block is used as a new value.
How to merge array of hash based on the same keys in ruby?
Try
a.flat_map(&:entries)
.group_by(&:first)
.map{|k,v| Hash[k, v.map(&:last)]}
Rails: How to merge two hashes if a specific key has the same value?
The following code would work for your given example.
code
result = arr.group_by {|h| h[:id]}.values.map do |arr|
arr.reduce do |h1, h2|
h1.merge(h2) do |k, ov, nv|
ov.eql?(nv) ? ov : [ov, nv].join(",")
end
end
end
p result
#=>[{:id=>77, :member_phone=>"9876543210,123456789", :created_at=>"2017-05-03T11:06:03.000Z", :name=>"Sure"}, {:id=>78, :member_phone=>"12345", :created_at=>"2017-05-03T11:06:03.000Z", :name=>"XYZ"}]
Combine 2 hashes with the same key
First, I think by {"12 am"=> 0, 0}
you mean a Hash with each of its keys being an hour and each value an Array with two elements, one representing the number of cleans in that hour and the other the number of lubes. If that's the case it should be like this: {"12 am"=> [0, 0]}
(with [
and ]
around each value)
You can do that with something like this:
@count_by_hour = @clean_by_hour.keys.each_with_object({}) do |k, h|
h[k] = [@clean_by_hour[k], @lube_by_hour[k]]
end
@count_by_hour # =>
{"12 am"=>[0, 0],
"1 am"=>[0, 0],
"2 am"=>[0, 0],
"3 am"=>[0, 0],
"4 am"=>[0, 0],
"5 am"=>[0, 0],
"6 am"=>[0, 0],
"7 am"=>[0, 0],
"8 am"=>[4, 3],
"9 am"=>[14, 4],
"10 am"=>[19, 10],
"11 am"=>[10, 14],
"12 pm"=>[19, 10],
"1 pm"=>[16, 8],
"2 pm"=>[13, 5],
"3 pm"=>[18, 20],
"4 pm"=>[7, 4],
"5 pm"=>[4, 2],
"6 pm"=>[4, 0],
"7 pm"=>[0, 0],
"8 pm"=>[0, 0],
"9 pm"=>[0, 0],
"10 pm"=>[0, 0],
"11 pm"=>[0, 0]}
You could also use a Hash for each value to improve the readability and prevent bugs: (It's easier to remember which Hash key to use than which Array index.)
@count_by_hour = @clean_by_hour.keys.each_with_object({}) do |k, h|
h[k] = { clean: @clean_by_hour[k], lube: @lube_by_hour[k] }
end
@count_by_hour # =>
{"12 am"=>{:clean=>0, :lube=>0},
"1 am"=>{:clean=>0, :lube=>0},
"2 am"=>{:clean=>0, :lube=>0},
"3 am"=>{:clean=>0, :lube=>0},
"4 am"=>{:clean=>0, :lube=>0},
"5 am"=>{:clean=>0, :lube=>0},
"6 am"=>{:clean=>0, :lube=>0},
"7 am"=>{:clean=>0, :lube=>0},
"8 am"=>{:clean=>4, :lube=>3},
"9 am"=>{:clean=>14, :lube=>4},
"10 am"=>{:clean=>19, :lube=>10},
"11 am"=>{:clean=>10, :lube=>14},
"12 pm"=>{:clean=>19, :lube=>10},
"1 pm"=>{:clean=>16, :lube=>8},
"2 pm"=>{:clean=>13, :lube=>5},
"3 pm"=>{:clean=>18, :lube=>20},
"4 pm"=>{:clean=>7, :lube=>4},
"5 pm"=>{:clean=>4, :lube=>2},
"6 pm"=>{:clean=>4, :lube=>0},
"7 pm"=>{:clean=>0, :lube=>0},
"8 pm"=>{:clean=>0, :lube=>0},
"9 pm"=>{:clean=>0, :lube=>0},
"10 pm"=>{:clean=>0, :lube=>0},
"11 pm"=>{:clean=>0, :lube=>0}}
Combining two hashes with a common key
What about:
h1 = { "a" => 10, "b" => 20, "c"=>34, "d"=>3}
h2 = { "a" => 11, "b" => 21, "d"=>15}
p h1.merge(h2){|key, old, new| Array(old).push(new) } #=> {"a"=>[10, 11], "b"=>[20, 21], "c"=>34, "d"=>[3, 15]}
And this is how I would write it to combine more than 2 Hashes:
h1 = { "a" => 10, "b" => 20, "c"=>34, "d"=>3}
h2 = { "a" => 11, "b" => 21, "d"=>15}
h3 = { "a" => 11, "b" => 21, "c"=> 1, "d"=>15}
merge_to_array = -> x,y { x.merge(y){|key, old, new| Array(old).push(new)} }
p [h1,h2,h3].reduce &merge_to_array #=> {"a"=>[10, 11, 11], "b"=>[20, 21, 21], "c"=>[34, 1], "d"=>[3, 15, 15]}
How can I merge two hashes without overwritten duplicate keys in Ruby?
If you have two hashes, options
and defaults
, and you want to merge defaults
into options
without overwriting existing keys, what you really want to do is the reverse: merge options
into defaults
:
options = defaults.merge(options)
Or, if you're using Rails you can do:
options.reverse_merge!(defaults)
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.
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(", ") }
Related Topics
Simulating Race Conditions in Rspec Unit Tests
Rails Routing: Giving Default Values for Path Helpers
Ruby: How to Join Elements of an Array Together with a Prefix
How to Create a Folder (If Not Present) with Logger.New
Is There an Easy-To-Use Ftp Library for Ruby
How to Import Using Fastercsv a Row with a Name Like "CiaráN"
Different Background Color for Different Pages in Rails
Emacs Ruby Method Parameter Indentation
Using Rvm, But Can't Set Current Ruby Version (Ubuntu 11.10)
How to Refresh a Page with Turbolinks
How to Store a Ruby Array into a File
Error While Installing Ruby 1.9.3
Installing Pl/Ruby for Postgresql 8.3
How to Pass Named Arguments to a Rake Task
Retrieve All Posts Where the Given User Has Commented, Ruby on Rails