2011-03-04 140 views
4

我如何把這個:如何根據散列值合併兩個散列數組?

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"} 
] 

進入這個:

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"} 
] 

我發現在如此相似的問題,但沒有一個是因爲這一個簡單。我可能會使用一種我不知道的方法/塊組合。

回答

3
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 
1

這確實你需要:

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 

N.B:在這裏,我假設first_array一直與nil:countsecond_array沒有,因爲在你的例子哈希值。

1

我的建議是使用:date作爲散列的關鍵字,並將:count作爲它的值。 如果你需要它的日期計算這將是最好有類似:

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

順便說一句,如果所需的輸出是一個,我建議這個解決方案:

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

此解決方案將優先考慮非零值

 

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 

2

TL; DR

使用each_with_object枚舉:

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

龍answe,R

一種方法我覺得有用的是each_with_object枚舉。整個方法可以寫成這樣:

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"}] 

,當我們需要沿着多個值的比較,這樣的做法也將工作,如a.none?{ |i| i[:date] == e[:date] and i[:location] == e[:location] }

當數組元素是隻有兩個鍵的散列且其中一個鍵是唯一的時,將數組轉換爲散列是另一種解決方案。我們首先將兩個數組轉換爲散列,然後將第一個數組與第二個數組合並,然後將其轉換回散列數組。

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}] 

第一種方法似乎更有效,但:

#!/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)