2016-12-15 53 views
1

我有以下structur的哈希:紅寶石合併n個哈希元素,並採取中間鍵

{ 
    timestamp1 => { attr1 => 1, attri2 => 2, attr3 => 3}, 
    timestamp2 => { attr1 => 1, attri2 => 2, attr3 => 3}, 
    timestamp3 => { attr1 => 1, attri2 => 2, attr3 => 3}, 
    ... 
} 

我要做的是總結n個元素在一起,取中間時間戳。 因此,例如,在這種情況下,如果將3天合併在一起,我想這樣的結果:

{ 
    timestamp2 => {attr1 => 3. attr2 => 6, attr3 => 9} 
    timestamp5 => ... # Here the next summed values 
    ... 
} 

我這樣做,現在媒體鏈接,它是工作,但代碼是不完全好的,它只是將合併直到只剩下中間一個第一和最後一個元素是左:

hash.each_slice(n) do |part| 
    iterator = 0 

    while(part.length > 1) do 
    hash_a = part.first.last 
    hash_b = part.last.last 

    new_values = hash_a.merge(hash_b){ | key, oldval, newval| (newval + oldval) 

    if iteration % 2 == 0 
    part[0][1] = new_values 
    part.delete_at(part.count-1) 
    else 
    part.delete_at(0) 
    part[part.count-1][1] = new_values 
    end 
    iteration += 1 
end 

注:我知道這個代碼是不漂亮,但你沒有糾正這一點,它僅僅是一個例子,不是我的實際代碼。

現在我想知道的。有沒有更好的方法來實現這一點,有些減少,選擇,group_by或合併。如果我能擺脫while循環,那將是非常好的。

預先感謝您。

+0

當你給出一個例子時,給每個輸入分配一個變量是很有幫助的(例如'h = {timestamp1 => ...')。這樣,讀者可以引用變量(例如'h'),而不必定義它。其次,沒有'......'。請。完成示例並顯示期望的結果,以便提供解決方案的人員可以演示其代碼生成結果。儘可能縮短範例,但仍然覆蓋基地。 –

+0

你所說的數組是散列。您的散列鍵似乎是未標註的局部變量或方法。您需要顯示這些變量或方法的代碼,或者將鍵全部寫成文字(例如符號或字符串)。無論如何,散列必須是有效的Ruby對象。當散列的大小不是組大小的倍數時,您還需要告訴我們您需要什麼,以及每個組中元素的數量是多少時使用哪個鍵。例如,如果該組有四個元素,那麼我們應該使用組的第二個還是第三個元素的鍵? –

+0

@CarySwoveland是的,這是一個散列,對不起,在思考之前打字。但其餘的,正如我所說,這是示例代碼,而不是我實際做的或完成的。我完全知道,這些不是有效的散列鍵。就像我說的,我不需要糾正這些代碼。同樣對於答案,無論你使用大小合適的散列表做什麼都沒有關係,因爲你們都不是在這裏寫我的代碼,只是爲了幫助我以正確的方式;-)但無論如何謝謝你看我的情況! –

回答

5
N = 3 

hash.each_slice(N).map do |part| 
    [part[part.size/2].first, part.each_with_object({}) do |(_, v), acc| 
    acc.merge!(v) { |_, v1, v2| v1 + v2 } 
    end] 
end.to_h 
+0

非常非常有趣,非常感謝你的答案。不幸的是,我可以看到我的散列沒有變化。但我認爲這不是你的方法的問題,但與我的代碼。我會再試一試,一旦我能夠運行它,接受你的回答。無論如何,我已經從閱讀中學到了一些新的東西。謝謝! –

+0

上面的代碼不會修改就地散列。這根本不是有意的。它生成一個新的哈希實例。您可能會重新分配您的原始變量,或者在清除後將其修改爲'clear!.merge!'。 – mudasobwa

+0

不,不,絕對沒問題,我理解,在此期間;-)。我在數據中發現了所有的錯誤,並且你的答案是我搜索到的內容。只有改善(或者至少在我的情況下) - >我用部分[part.count/2]替換了部分[N/2],使其通過不完整的組運行。 (我使用日期鍵,並間隔切片,而不是每個集都完成)。 –

1

代碼

def group(h,n) 
    h.each_slice(n).with_object({}) do |a,g| 
    key = a[(a.size-1)/2].first 
    a.each {|k,f| g.update(key=>f) {|_,oh,nh| oh.merge(nh) {|_,ov,nv| ov+nv}}} 
    end 
end 

這不會發生變異h

爲了證明代碼我將使用含有鍵文字的散列。

h = { 
    :timestamp1 => { :attr1 => 1, :attri2 => 2, :attr3 => 3}, 
    :timestamp2 => { :attr1 => 1, :attri2 => 2, :attr3 => 3}, 
    :timestamp3 => { :attr1 => 1, :attri2 => 2, :attr3 => 3}, 
    :timestamp4 => { :attr1 => 4, :attri2 => 5, :attr3 => 6}, 
    :timestamp5 => { :attr1 => 4, :attri2 => 5, :attr3 => 6}, 
    :timestamp6 => { :attr1 => 4, :attri2 => 5, :attr3 => 6}, 
} 

group(h,1) == h 
    #=> true 
group(h,2) 
    #=> {:timestamp1=>{:attr1=>2, :attri2=>4, :attr3=>6}, 
    # :timestamp3=>{:attr1=>5, :attri2=>7, :attr3=>9}, 
    # :timestamp5=>{:attr1=>8, :attri2=>10, :attr3=>12}} 
group(h,3) 
    #=> {:timestamp2=>{:attr1=>3, :attri2=>6, :attr3=>9}, 
    # :timestamp5=>{:attr1=>12, :attri2=>15, :attr3=>18}} 
group(h,4) 
    #=> {:timestamp2=>{:attr1=>7, :attri2=>11, :attr3=>15}, 
    # :timestamp5=>{:attr1=>8, :attri2=>10, :attr3=>12}} 
group(h,5) 
    #=> {:timestamp3=>{:attr1=>11, :attri2=>16, :attr3=>21}, 
    # :timestamp6=>{:attr1=>4, :attri2=>5, :attr3=>6}} 
group(h,6) 
    #=> {:timestamp4=>{:attr1=>15, :attri2=>21, :attr3=>27}} 

說明

我已經使用的Hash#update(又名Hash.merge!)和Hash#merge僱用一個塊,以確定存在於被合併兩個散列密鑰的值的形式。 (「舊值」),nh(「新值」),ov(「舊值」)和nv(「新值」)參見文檔以解釋塊變量_(常用密鑰),oh

步驟如下。

n = 3 
enum0 = h.each_slice(n) 
    #=> #<Enumerator: {:timestamp1=>{:attr1=>1, :attri2=>2, :attr3=>3}, 
    #     :timestamp2=>{:attr1=>1, :attri2=>2, :attr3=>3}, 
    #     ... 
    #     :timestamp6=>{:attr1=>4, :attri2=>5, :attr3=>6}} 
    #  :each_slice(3)> 
enum1 = enum0.with_object({}) 
    #=> #<Enumerator: 
    #  #<Enumerator: {:timestamp1=>{:attr1=>1, :attri2=>2, :attr3=>3}, 
    #     :timestamp2=>{:attr1=>1, :attri2=>2, :attr3=>3}, 
    #     ... 
    #     :timestamp6=>{:attr1=>4, :attri2=>5, :attr3=>6}} 
    #  :each_slice(3)>:with_object({})> 

我們可以檢查將被enum1通過轉換enum1到一個陣列產生並傳遞到該塊中的(二)的元件。

enum1.to_a 
    #=> [ 
    #  [ 
    #  [ 
    #   [:timestamp1, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #   [:timestamp2, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #   [:timestamp3, {:attr1=>1, :attri2=>2, :attr3=>3}]], 
    #   {} 
    #  ], 
    #  [ 
    #  [ 
    #   [:timestamp4, {:attr1=>4, :attri2=>5, :attr3=>6}], 
    #   [:timestamp5, {:attr1=>4, :attri2=>5, :attr3=>6}], 
    #   [:timestamp6, {:attr1=>4, :attri2=>5, :attr3=>6}]], 
    #   {} 
    #  ] 
    #  ] 

空數組由塊變量g表示。該方法將構建並返回。將由enum1生成的第一個元素傳遞給該塊,並使用並行分配分配兩個塊變量。

a,g = enum1.next 
    #=> [ 
    #  [ 
    #  [:timestamp1, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #  [:timestamp2, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #  [:timestamp3, {:attr1=>1, :attri2=>2, :attr3=>3}] 
    #  ] 
    #  {} 
    # ] 
a #=> [ 
    #  [:timestamp1, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #  [:timestamp2, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #  [:timestamp3, {:attr1=>1, :attri2=>2, :attr3=>3}] 
    # ] 
g #=> {} 

現在可以執行塊計算。

key = a[(a.size-1)/2].first 
    #=> a[2].first 
    #=> [:timestamp2, {:attr1=>1, :attri2=>2, :attr3=>3}].first 
    #=> :timestamp2 
enum2 = a.each 
    #=> #<Enumerator: [[:timestamp1, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #     [:timestamp2, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    #     [:timestamp3, {:attr1=>1, :attri2=>2, :attr3=>3}]] 
    #  :each> 
enum2.to_a # for display purposes only 
    #=> [[:timestamp1, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    # [:timestamp2, {:attr1=>1, :attri2=>2, :attr3=>3}], 
    # [:timestamp3, {:attr1=>1, :attri2=>2, :attr3=>3}]] 
k,f = enum2.next 
    #=> [:timestamp1, {:attr1=>1, :attri2=>2, :attr3=>3}] 
k #=> :timestamp1 
f #=> {:attr1=>1, :attri2=>2, :attr3=>3} 
g.update(key=>f) {|_,oh,nh| 
    oh.merge(nh) {|_,ov,nv| g.update(key=>f) {|_,oh,nh| oh.merge(nh) {|_,ov,nv| ov+nv}}}} 
    #=> g.update(:timestamp2=>{:attr1=>1, :attri2=>2, :attr3=>3}) {|_,oh,nh| 
    #  oh.merge(nh) {|_,ov,nv| ov+nv}}}} 
    #=> {:timestamp2=>{:attr1=>1, :attri2=>2, :attr3=>3}} 

然後

k,f = enum2.next 
    #=> [:timestamp2, {:attr1=>1, :attri2=>2, :attr3=>3}] 
k #=> :timestamp2 
f #=> {:attr1=>1, :attri2=>2, :attr3=>3} 
g.update(key=>f) {|_,oh,nh| oh.merge(nh) {|_,ov,nv| ov+nv}} 
    # g.update(:timestamp2=>{:attr1=>1, :attri2=>2, :attr3=>3} {|_,oh,nh| 
    # oh.merge(nh) {|_,ov,nv| ov+nv}} 
    #=> {:timestamp2=>{:attr1=>2, :attri2=>4, :attr3=>6}} 

其餘的計算是相似的。

1公用密鑰用下劃線表示,表示它在塊計算中不使用。

+0

感謝您的詳細解答。 –