2014-12-04 56 views
1

我有一個哈希,其中大部分填充了一個與該關鍵字相關聯的兩個值的關鍵字。在這個哈希中還有另一個哈希,這是我被卡住的地方。通過散列迭代輸出一個無序列表

比方說散的樣子:

{'sports'=>['football', 'basketball'], 'season'=>['summer','fall'], 'data'=>[{'holiday'=>'Christmas', 'genre' => 'Comedy'}, {'holiday'=>'Thanksgiving', 'genre' => 'Action'}]} 

輸出應該是這樣的:

Sports 
    - football 
    - basketball 
Season 
    - summer 
    - fall 
Holiday 
    - Christmas 
    - Thanksgiving 
Genre 
    - Comedy 
    - Action 

到目前爲止,我有一個助手,讓我除了data部分的一切。

def output_list_from(hash) 
    return if hash.empty? 

    content_tag(:ul) do 
    hash.map do |key, values| 
     content_tag(:li, key.to_s.humanize) + 

     content_tag(:ul) do 
     # if values.is_a?(Hash)... 
     content_tag(:li, values.first) + 
     content_tag(:li, values.last) 
     end 
    end.join.html_safe 
    end.html_safe 
end 

這將返回輸出:

Sports 
    - football 
    - basketball 
Season 
    - summer 
    - fall 
Data 
    - {'holiday'=>'Christmas', 'genre' => 'Comedy'} 
    - {'holiday'=>'Thanksgiving', 'genre' => 'Action'} 

這當然是有道理的......所以我試着在循環來檢查value是一個Hash,但這樣它的成立欺騙了我。我認爲如果我知道每次散列的樣子會更容易,但每次都會有新的散列。有一次,數據內可能有holiday,另一次可能有holidaygenre

任何意見,將不勝感激。

+0

總是在'data'哈希值的陣列? – 2014-12-04 17:37:16

+0

'數據'不總是*在散列中。如果'data'存在,那麼是的,它將是一個散列數組。 – 2014-12-04 17:38:23

回答

2

您將需要創建正確格式的散列。事情是這樣的:

hash = {'sports'=>['football', 'basketball'], 'season'=>['summer','fall'], 'data'=>[{'holiday'=>'Christmas', 'genre' => 'Comedy'}, {'holiday'=>'Thanksgiving', 'genre' => 'Action'}]} 
formatted_data = hash.dup 

data = formatted_data.delete('data') 
if data 
    data.each do |item| 
    item.each do |k, v| 
     formatted_data[k] ||= [] 
     formatted_data[k] << v 
    end 
    end 
end 

puts formatted_data 
# => {"sports"=>["football", "basketball"], "season"=>["summer", "fall"], 
# => "holiday"=>["Christmas", "Thanksgiving"], "genre"=>["Comedy", "Action"]} 

content_tag(:ul) do 
    formatted_data.map do |key, values| 
    #... your code here... 
    end.join.html_safe 
end.html_safe 
+0

令人印象深刻。謝謝你。 – 2014-12-04 18:05:03

1

假設你的散列是這樣的:

hash = { 'sports'=>['football', 'basketball'], 
     'season'=>['summer', 'fall'], 
     'data1' =>[{ 'holiday'=>'Christmas', 'genre'=>'Comedy'}, 
        { 'holiday'=>'Thanksgiving', 'genre'=>'Action' }], 
     'data2' =>[{ 'data3'=>[{ 'sports'=>'darts', 'genre'=>'Occult' }] }] 
     } 

你希望將適用於任何數量的等級,並且不依賴於按鍵的名稱的通用解決方案將不在所得到的散列中(這裏'data1','data2''data3')。使用遞歸可以做到這一點。

代碼

def extract(h, new_hash = {}) 
    h.each do |k,v| 
    [*v].each do |e| 
     case e 
     when Hash then extract(e, new_hash) 
     else new_hash.update({ k=>[e] }) { |_,ov,nv| ov << nv.first } 
     end 
    end 
    end 
    new_hash 
end 

extract(hash) 
    #=> {"sports"=>["football", "basketball", "darts"], 
    # "season"=>["summer", "fall"], 
    # "holiday"=>["Christmas", "Thanksgiving"], 
    # "genre"=>["Comedy", "Action", "Occult"]} 

說明

還有,我認爲,可能需要澄清的代碼主要是兩件事情。

#1

首先是相當孤獨和奇數尋找表達:

[*v] 

如果v是一個數組,這將返回v。如果v是文字,則splat運算符不起作用,因此它返回[v]。換句話說,它只保留數組並將文本轉換爲包含一個元素的數組本身。人機工程學:

[*['football', 'basketball']] #=> ["football", "basketball"] 
[*'Thanksgiving']    #=> ["Thanksgiving"] 

這節省了我們有三個,而不是兩個,在case聲明可能的麻煩。我們只需將文字轉換爲一個元素的數組,就可以處理散列和數組。

#2

第二片段,可能不熟悉的一些是:

new_hash.update({ k=>[e] }) { |_,ov,nv| ov << nv.first } 

這使用方法Hash#update(又名merge!)的形式,其使用一個塊,以解決兩個哈希中存在的鍵的值被合併。作爲一個例子,在計算中的某些階段,new_hash將有一個鍵 - 值對:

'sports'=>['football', 'basketball'] 

,並且是與所述散列進行更新:

{ 'sports'=>['darts'] } 

由於這兩種

{ |k,ov,nv| ov << nv.first } 
    #=> { |'sport', ['football', 'basketball'], ['darts']| ov << nv.first } 
    #=> { |'sport', ['football', 'basketball'], ['darts']| 
      ['football', 'basketball'] << 'darts' } 
    #=> ['football', 'basketball'] << 'darts' 

,因爲我不使用鑰匙:哈希有鑰匙'sport',該塊被作爲時叫仲裁者塊中,我把它換成與一個佔位符(_)塊變量,以減少出錯機會,並告知該密鑰不被使用的閱讀器。

1 I有時使用飛鏢作爲例子的運動的,因爲它是少數,其中一個可以成功而不極其身體健康的一個。

+0

也令人印象深刻。感謝您在@Cary上的時間。 – 2015-01-24 19:51:03