2014-12-04 126 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