Ruby-哈希数组-从哈希值中嵌套HTML菜单,无需写入重复项
我有一个相对较大的散列,其中所有键的值都是散列数组(见下面的示例布局)。在过去的4.5个小时里,我一直在尝试为我的Rails应用程序编写HTML,我真的快要哭了,因为我一直在兜圈子,却没有任何东西可以展示出来 非常感谢您的帮助,并提前感谢您的时间 遇到的具体问题Ruby-哈希数组-从哈希值中嵌套HTML菜单,无需写入重复项,html,ruby,hash,nested,Html,Ruby,Hash,Nested,我有一个相对较大的散列,其中所有键的值都是散列数组(见下面的示例布局)。在过去的4.5个小时里,我一直在尝试为我的Rails应用程序编写HTML,我真的快要哭了,因为我一直在兜圈子,却没有任何东西可以展示出来 非常感谢您的帮助,并提前感谢您的时间 遇到的具体问题 章节/诗句出现在它们不对齐的书籍上 我也无法消除重复条目,例如,“第4章”出现了多次(而不是一次出现,下面嵌套了多个章节/诗句引用) 所需的解决方案标准 HTML需要分3层/嵌套编写(第一层/包含书名的div(如《创世纪》、《出埃及记》
hsh
的键顺序书写(如《创世记》后面跟着《出埃及记》,后面跟着《利未记》,而不是按字母顺序)hsh = { :genesis =>
[
{:id => 1, :verse => 'Genesis 4:12'},
{:id => 1, :verse => 'Genesis 4:23-25'},
{:id => 2, :verse => 'Genesis 6:17'}
],
:exodus =>
[
{:id => 5, :verse => 'Exodus 2:7'},
{:id => 3, :verse => 'Exodus 2:14-15'},
{:id => 4, :verse => 'Exodus 12:16'}
],
:leviticus =>
[
{:id => 2, :verse => 'Leviticus 11:19-21'},
{:id => 7, :verse => 'Leviticus 15:14-31'},
{:id => 7, :verse => 'Leviticus 19:11-12'}
]
}
所需的输出HTML[为简洁起见缩短]
<div class="submenu">
<a href="#">Genesis</a>
<div class="lvl-2">
<div>
<div class="submenu">
<a>Chapter 4</a>
<div class="lvl-3">
<div>
<a onclick="load('1')"><span>ID 1</span> Verse 12</a>
<a onclick="load('1')"><span>ID 1</span> Verse 23-25</a>
</div>
</div>
</div>
<div class="submenu">
<a>Chapter 6</a>
<div class="lvl-3">
<div> <a onclick="load('2')"><span>ID 2</span> Verse 17</a> </div>
</div>
</div>
</div>
</div>
<div class="submenu">
<a href="#">Exodus</a>
<div class="lvl-2">
<div>
<div class="submenu">
<a>Chapter 2</a>
<div class="lvl-3">
<div>
<a onclick="load('5')"><span>ID 5</span> Verse 7</a>
<a onclick="load('3')"><span>ID 3</span>Verse 14-15</a>
</div>
</div>
</div>
<div class="submenu">
<a>Chapter 12</a>
<div class="lvl-3">
<div>
<a onclick="load('4')"><span>ID 4</span> Verse 16</a>
</div>
</div>
</div>
</div>
</div>
## Shortened for brevity (Leviticus references excluded)
</div>
</div>
第二章
ID 5第7节
ID 3第14-15节
第十二章
ID 4第16节
##为简洁而缩短(利未记参考文献除外)
代码
final_html = String.new
hsh.each do |book, verse_array|
verse_array.each do |reference|
book = reference[:verse].split(' ').first # => "Genesis"
full_verse = reference[:verse].split(' ').last # => "4:12"
chapter = full_verse.split(':').first # => "4"
verse = full_verse.split(':').first # => "12"
# Failing Miserably at appending the right HTML to the final_html string here...
# final_html << "<div class=\"submenu\">"
# final_html << ...
# final_html << ...
end
end
final\u html=String.new
每一本书,每一首诗|
韵文|数组。每个do |引用|
书=参考文献[:韵文]。拆分(“”)。第一个#=>“创世记”
全文=参考[:韵文]。拆分(“”)。最后一个#=>“4:12”
章=完整的诗。分开(“:”)。第一个“=>”4“
韵文=完整韵文。拆分(“:”)。第一个#=>“12”
#在这里将正确的HTML附加到最后的HTML字符串时失败得很惨。。。
#final_html与此类问题的常见情况一样,一旦将数据放入与所需输出非常相似的“形状”中,解决问题就会变得容易得多。您的输出是嵌套的树结构,因此您的数据也应该是嵌套的。让我们先做吧。以下是您的数据:
data = {
genesis: [
{ id: 1, verse: "Genesis 4:12" },
{ id: 1, verse: "Genesis 4:23-25" },
{ id: 2, verse: "Genesis 6:17" }
],
exodus: [
{ id: 5, verse: "Exodus 2:7" },
# ...
],
# ...
}
现在暂且不考虑HTML,下面是我们想要的结构:
Genesis
第四章
ID 1-第12节
ID 1-第23-25节
第六章
ID 2-第17节
离去
第二章
ID 5-第7节
...
...
关于您的数据,我注意到的第一件事是,它具有您不需要的嵌套级别。由于:verse
值包含书名,因此我们不需要外部散列中的键(:genesis
等),只需将所有内部散列展平为一个数组:
data.values.flatten
# => [ { id: 1, verse: "Genesis 4:12" },
# { id: 1, verse: "Genesis 4:23-25" },
# { id: 2, verse: "Genesis 6:17" }
# { id: 5, verse: "Exodus 2:7" },
# # ... ]
现在我们需要一种方法来从:verse
字符串中提取书籍、章节和韵文。如果需要,可以使用String#split
,但Regexp也是一个不错的选择:
VERSE_EXPR = /(.+)\s+(\d+):(.+)$/
def parse_verse(str)
m = str.match(VERSE_EXPR)
raise "Invalid verse string!" if m.nil?
book, chapter, verse = m.captures
{ book: book, chapter: chapter, verse: verse }
end
flat_data = data.values.flatten.map do |id:, verse:|
{ id: id }.merge(parse_verse(verse))
end
# => [ { id: 1, book: "Genesis", chapter: "4", verse: "12" },
# { id: 1, book: "Genesis", chapter: "4", verse: "23-25" },
# { id: 2, book: "Genesis", chapter: "6", verse: "17" },
# { id: 5, book: "Exodus", chapter: "2", verse: "7" },
# # ... ]
现在可以很容易地按书本对数据进行分组:
books = flat_data.group_by {|book:, **| book }
# => { "Genesis" => [
# { id: 1, book: "Genesis", chapter: "4", verse: "12" },
# { id: 1, book: "Genesis", chapter: "4", verse: "23-25" },
# { id: 2, book: "Genesis", chapter: "6", verse: "17" }
# ],
# "Exodus" => [
# { id: 5, book: "Exodus", chapter: "2", verse: "7" },
# # ...
# ],
# # ...
# }
…在每本书中,按章节:
books_chapters = books.map do |book, verses|
[ book, verses.group_by {|chapter:, **| chapter } ]
end
# => [ [ "Genesis",
# { "4" => [ { id: 1, book: "Genesis", chapter: "4", verse: "12" },
# { id: 1, book: "Genesis", chapter: "4", verse: "23-25" } ],
# "6" => [ { id: 2, book: "Genesis", chapter: "6", verse: "17" } ]
# }
# ],
# [ "Exodus",
# { "2" => [ { id: 5, book: "Exodus", chapter: "2", verse: "7" },
# # ... ],
# # ...
# }
# ],
# # ...
# ]
您会注意到,因为我们在books
上调用了map
,所以我们的最终结果是一个数组,而不是散列。您可以调用使其再次成为散列,但出于我们的目的,这是不必要的(迭代键值对数组的工作原理与迭代散列的工作原理相同)
它看起来有点凌乱,但你可以看到结构在那里:诗句嵌套在书籍的章节中。现在我们只需要把它转换成HTML
顺便说一句,为了我们的残疾朋友:正确的
用于嵌套树结构的HTML元素是
或
。如果
您需要使用
s,但是
否则,请为使用
无障碍设备将感谢您。(已经写了许多文章
关于设计这样的树的样式,所以我不会深入讨论,但首先你可以
使用列表样式类型隐藏项目符号:无;
)
我没有可以使用的Rails应用程序,所以为了生成HTML,我只使用Ruby标准库中的ERB。除了如何将变量传递给视图之外,它在Rails中看起来或多或少是相同的
require "erb"
VIEW = <<END
<ul>
<% books.each do |book_name, chapters| %>
<li>
<a href="#"><%= book_name %></a>
<ul>
<% chapters.each do |chapter, verses| %>
<li>
<a href="#">Chapter <%= chapter %></a>
<ul>
<% verses.each do |id:, verse:, **| %>
<li>
<a onclick="load(<%= id %>)">ID <%= id %>: Verse <%= verse %></a>
</li>
<% end %>
</ul>
</li>
<% end %>
</ul>
</li>
<% end %>
</ul>
END
def render(books)
b = binding
ERB.new(VIEW, nil, "<>-").result(b)
end
puts render(books_chapters)
需要“erb”
VIEW=All,我意识到我提供的代码没有显示出太多的努力,但我向你保证,我花了数小时尝试各种迭代策略,等等。现在在Rubymine中,我有大约210行垃圾,大部分都被注释掉了,试图解释我尝试过的所有技术会让这个问题变得太长。它已经可以说是在更长的一边。再次感谢你的帮助。你有什么错误吗?嗨,迪内什,不是很明确。我以多种不同的方式迭代了散列数组,生成的HTML没有任何错误——它的顺序和重复性非常糟糕。谢谢你伸出援手。非常感谢。你能帮忙吗?我比你想象的更爱你。。。非常感谢你写了这篇详细的、冗长的、可能会让人筋疲力尽的文章。非常感谢你的时间。我今晚或很快就会试试这个!乔丹,我想我还没有获得PM的许可,否则我会直接问。你可以接受捐款来帮助别人吗?如果是这样,请提供您的贝宝或类似!谢谢你,乔丹。这很有效。请注意,在早期版本中