我有一个Hash,其中大部分都填充了一个带有两个与键关联的值的键。在这个Hash中还有另一个哈希,这是我被困住的地方。
让我们说哈希看起来像:
{'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
是否是哈希,但是它的设置方式欺骗了我。我认为如果我知道哈希每次都会是什么样子会更容易,但每次都会是一个新的哈希值。一次可能有holiday
个数据,另一次可能有holiday
和genre
。
任何建议都将受到赞赏。
答案 0 :(得分: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
答案 1 :(得分: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'
和{ {1}})。这是使用递归的一种方法。
<强>代码强>
'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
<强>解释强>
我认为,代码中有两件事可能需要澄清。
#1
第一个是相当孤独和奇怪的表情:
extract(hash)
#=> {"sports"=>["football", "basketball", "darts"],
# "season"=>["summer", "fall"],
# "holiday"=>["Christmas", "Thanksgiving"],
# "genre"=>["Comedy", "Action", "Occult"]}
如果[*v]
是数组,则返回v
。如果v
是文字,则splat运算符无效,因此返回v
。换句话说,它只留下数组并将文字转换为包含一个元素的数组本身。埃尔戈:
[v]
这使我们省去了[*['football', 'basketball']] #=> ["football", "basketball"]
[*'Thanksgiving'] #=> ["Thanksgiving"]
语句中有三种而不是两种可能性的麻烦。我们只是将文字转换为一个元素的数组,允许我们只处理哈希和数组。
#2
可能对某些人不熟悉的第二个片段是:
case
这使用方法Hash#update(a.k.a。new_hash.update({ k=>[e] }) { |_,ov,nv| ov << nv.first }
)的形式,该方法使用块来解析合并的两个哈希中存在的键的值。例如,在计算的某个阶段,merge!
将具有键值对:
new_hash
并将使用散列 1 :
进行更新'sports'=>['football', 'basketball']
由于这两个哈希值都具有键{ 'sports'=>['darts'] }
,因此该块被称为仲裁器:
'sport'
由于我没有在块中使用密钥{ |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我有时会使用飞镖作为运动的例子,因为它是少数几个可以在没有身体健康的情况下获得成功的人之一。