2014-03-27 89 views
0

我想基於特定的鍵/值對合並散列數組。基於ruby中的特定鍵/值對合並散列

array = [ {:id => '1', :value => '2'}, {:id => '1', :value => '5'} ] 

我希望的輸出是

{:id => '1', :value => '7'} 

作爲patru指出,在SQL而言,這將等效於:

SELECT SUM(value) FROM Hashes GROUP BY id 

換句話說,我的陣列包含記錄的哈希。我想獲得特定字段的總和,但總和會按鍵/值對分組。換句話說,如果我的選擇標準是:id,如上面的示例中所示,那麼它會將哈希分爲ID相同的組和其他鍵的總和。

對於因錯字錯誤導致的任何混淆,我表示歉意。

+0

你可能需要與你的鑰匙一致的是,他們的字符串或符號 – bjhaid

+0

執行陣列中的所有哈希值有':id'和':value'鍵,是id總是1? – 2014-03-28 00:09:42

+0

我猜你喜歡* sum *具有特定':id'的散列的':value'屬性,就像你在SQL語句'SELECT SUM(value)FROM Hashes GROUP BY id'中得到的那樣,如果你得到了我意思。你能澄清一下嗎? – Patru

回答

1

編輯:自從我第一次發佈我的答案以來,問題已被澄清。結果,我大大修改了我的答案。

下面是解決這個問題的兩種「標準」方法。兩者都使用Enumerable#select首先從包含給定鍵/值對的數組(哈希)中提取元素。

#1

第一種方法使用Hash#merge!到每個數組元素(散列)順序地合併成一個散列最初爲空的。

代碼

def doit(arr, target_key, target_value) 
    qualified = arr.select {|h|h.key?(target_key) && h[target_key]==target_value} 
    return nil if qualified.empty? 
    qualified.each_with_object({}) {|h,g| 
    g.merge!(h) {|k,gv,hv| k == target_key ? gv : (gv.to_i + hv.to_i).to_s}} 
end 

arr = [{:id => '1', :value => '2'}, {:id => '2', :value => '3'}, 
     {:id => '1', :chips => '4'}, {:zd => '1', :value => '8'}, 
     {:cat => '2', :value => '3'}, {:id => '1', :value => '5'}] 

doit(arr, :id, '1') 
    #=> {:id=>"1", :value=>"7", :chips=>"4"} 

說明

這裏的關鍵是使用採用了塊的Hash#merge!版本,以確定該值每個鍵/ VA其中的鍵出現在兩個哈希中的lue pair被合併。該密鑰的兩個值由塊變量hvgv表示。我們只是想將它們加在一起。請注意,g是由each_with_object創建並由doit返回的(最初爲空)散列對象。

target_key = :id 
target_value = '1' 

qualified = arr.select {|h|h.key?(target_key) && h[target_key]==target_value} 
    #=> [{:id=>"1", :value=>"2"},{:id=>"1", :chips=>"4"},{:id=>"1", :value=>"5"}] 
qualified.empty? 
    #=> false 
qualified.each_with_object({}) {|h,g| 
    g.merge!(h) {|k,gv,hv| k == target_key ? gv : (gv.to_i + hv.to_i).to_s}} 
    #=> {:id=>"1", :value=>"7", :chips=>"4"} 

#2

其他常見的方式做這種計算是使用Enumerable#flat_map,其次是Enumerable#group_by

代碼

def doit(arr, target_key, target_value) 
    qualified = arr.select {|h|h.key?(target_key) && h[target_key]==target_value} 
    return nil if qualified.empty? 
    qualified.flat_map(&:to_a) 
      .group_by(&:first) 
      .values.map { |a| a.first.first == target_key ? a.first : 
      [a.first.first, a.reduce(0) {|tot,s| tot + s.last}]}.to_h 
end 

說明

這可能看起來複雜,但如果你將它分解爲步驟它不是那麼糟糕。這是發生了什麼。 (中qualified計算是一樣#1)

target_key = :id 
target_value = '1' 

c = qualified.flat_map(&:to_a) 
    #=> [[:id,"1"],[:value,"2"],[:id,"1"],[:chips,"4"],[:id,"1"],[:value,"5"]] 
d = c.group_by(&:first) 
    #=> {:id=>[[:id, "1"], [:id, "1"], [:id, "1"]], 
    # :value=>[[:value, "2"], [:value, "5"]], 
    # :chips=>[[:chips, "4"]]} 
e = d.values 
    #=> [[[:id, "1"], [:id, "1"], [:id, "1"]], 
    # [[:value, "2"], [:value, "5"]], 
    # [[:chips, "4"]]] 
f = e.map { |a| a.first.first == target_key ? a.first : 
    [a.first.first, a.reduce(0) {|tot,s| tot + s.last}] } 
    #=> [[:id, "1"], [:value, "7"], [:chips, "4"]] 
f.to_h => {:id=>"1", :value=>"7", :chips=>"4"} 
    #=> {:id=>"1", :value=>"7", :chips=>"4"} 

評論

你不妨從qualified在哈希整數考慮出不來的價值觀和排除target_key/target_value雙:

arr = [{:id => 1, :value => 2}, {:id => 2, :value => 3}, 
     {:id => 1, :chips => 4}, {:zd => 1, :value => 8}, 
     {:cat => 2, :value => 3}, {:id => 1, :value => 5}] 

target_key = :id 
target_value = 1 

qualified = arr.select { |h| h.key?(target_key) && h[target_key]==target_value} 
       .each { |h| h.delete(target_key) } 
    #=> [{:value=>2}, {:chips=>4}, {:value=>5}] 
return nil if qualified.empty? 

然後要麼

qualified.each_with_object({}) {|h,g| g.merge!(h) { |k,gv,hv| gv + hv } } 
    #=> {:value=>7, :chips=>4} 

qualified.flat_map(&:to_a) 
     .group_by(&:first) 
     .values 
     .map { |a| [a.first.first, a.reduce(0) {|tot,s| tot + s.last}] }.to_h 
    #=> {:value=>7, :chips=>4}