nil, :date=>"Feb 01"},...">

How to combine two hash arrays based on hash value?

How to do it:

first_array = [ {:count=>nil, :date=>"Jan 31"}, {:count=>nil, :date=>"Feb 01"}, {:count=>nil, :date=>"Feb 02"}, {:count=>nil, :date=>"Feb 03"}, {:count=>nil, :date=>"Feb 04"}, {:count=>nil, :date=>"Feb 05"} ] second_array = [ {:count=>12, :date=>"Feb 01"}, {:count=>2, :date=>"Feb 02"}, {:count=>2, :date=>"Feb 05"} ] 

In it:

 result = [ {:count=>nil, :date=>"Jan 31"}, {:count=>12, :date=>"Feb 01"}, {:count=>2, :date=>"Feb 02"}, {:count=>nil, :date=>"Feb 03"}, {:count=>nil, :date=>"Feb 04"}, {:count=>2, :date=>"Feb 05"} ] 

I found similar questions on SO, but none of them were as simple as this. Probably the method / block combination that I should use, I don't know.

+4
source share
5 answers
 result_array = first_array.map do |first_hash| second_array.each do |second_hash| if first_hash[:date] == second_hash[:date] first_hash[:count] = second_hash[:count] break end end first_hash end 
+3
source

TL DR

Use each_with_object enumerated:

 first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } } 

Long answer: r

One approach that I find useful is to list each_with_object . The whole method can be written as follows:

 first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } } irb(main):025:0> pp first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } } [{:count=>12, :date=>"Feb 01"}, {:count=>2, :date=>"Feb 02"}, {:count=>2, :date=>"Feb 05"}, {:count=>nil, :date=>"Jan 31"}, {:count=>nil, :date=>"Feb 03"}, {:count=>nil, :date=>"Feb 04"}] 

This approach will also work when we need to compare multiple values, for example. a.none?{ |i| i[:date] == e[:date] and i[:location] == e[:location] }

When the elements of an array are hashes with two keys, and one of the keys is unique, converting the array to a hash is another solution. First, we convert both arrays to hashes, and then combine the first with the second, and then convert it back to a hash array.

 def array_of_hashed_dates_to_hash(a_of_h); a_of_h.each_with_object({}){ |e,h| h[e[:date]] = e[:count] }; end array_of_hashed_dates_to_hash(first_array).merge(array_of_hashed_dates_to_hash(second_array)).map{|e| {date: e.first, count: e.last}} irb(main):039:0> pp array_of_hashed_dates_to_hash(first_array).merge(array_of_hashed_dates_to_hash(second_array)).map{|e| {date: e.first, count: e.last}} [{:date=>"Jan 31", :count=>nil}, {:date=>"Feb 01", :count=>12}, {:date=>"Feb 02", :count=>2}, {:date=>"Feb 03", :count=>nil}, {:date=>"Feb 04", :count=>nil}, {:date=>"Feb 05", :count=>2}] 

The first way seems more efficient:

 #!/usr/bin/ruby -Ku require 'benchmark' first_array = [ {:count=>nil, :date=>"Jan 31"}, {:count=>nil, :date=>"Feb 01"}, {:count=>nil, :date=>"Feb 02"}, {:count=>nil, :date=>"Feb 03"}, {:count=>nil, :date=>"Feb 04"}, {:count=>nil, :date=>"Feb 05"} ] second_array = [ {:count=>12, :date=>"Feb 01"}, {:count=>2, :date=>"Feb 02"}, {:count=>2, :date=>"Feb 05"} ] n = 1000 def array_of_hashed_dates_to_hash(a_of_h) a_of_h.each_with_object({}){ |e,h| h[e[:date]] = e[:count] } end Benchmark.bm(20) do |x| x.report("Compare by Hash value (each_with_object)") do n.times do first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } } end end x.report("Convert to Hashes and merge") do n.times do first_array_hash = array_of_hashed_dates_to_hash(first_array) second_array_hash = array_of_hashed_dates_to_hash(second_array) first_array_hash.merge(second_array_hash).map{|e| {date: e.first, count: e.last}} end end end user system total real Compare by Hash value (each_with_object) 0.000000 0.000000 0.000000 ( 0.008223) Convert to Hashes and merge 0.020000 0.000000 0.020000 ( 0.012077) 
+2
source

This does what you need:

 result = first_array.map do |first_hash| c = second_array.select do |second_hash| second_hash[:date] == first_hash[:date] end if c.empty? first_hash else c.first end end 

NB: Here, I assume that first_array always has hashes with nil :count , and second_array does not, as in your example.

+1
source

My suggestion is to use: date as the hash key and: count as its value. if all you need, counting on a date, it is better to have something like:

 result_hash = { "Jan 31" => nil, "Feb 01" => 12, ...} 

btw, if the required result is required, I propose this solution:

 all = first_array + second_array result_hash = {} all.each do |x| result_hash[x[:date]] = x[:count] end result = [] result_hash.each_pair do |x, y| result << {:count => y, :date => x} end 
+1
source

This solution will give priority to non nil values.

 def hash_merge (h1, h2)
   h3 = {}
   h1.each do | k, v |
     h3 [k] = h1 [k]. eql? (h2 [k])?  v: (h1 [k] .nil?? h2 [k]: h1 [k])
   end
   return h3
 end

 result = []
 first_array.each do | h1 |
   h2 = {}
   second_array.each do | h |
     if h1 [: date] .eql? (h [: date])
       h2 = h
       break
     end
   end
   result.push hash_merge (h1, h2)
 end
 p result

+1
source

Source: https://habr.com/ru/post/1342260/


All Articles