我有一个相对较大的散列,其中所有键的值都是散列数组(参见下面的示例布局)。我花了4.5个小时试图为我的Rails应用程序编写HTML,我真的要哭了,因为我一直在兜圈子,没有任何真正的东西可以展示!
任何帮助都是非常感谢提前感谢您的时间!
遇到的具体问题
- 章节/诗句出现在不对齐的书籍中。
- 我也不能删除重复的条目,所以'Chapter 4',例如,出现多次(而不是出现一次,下面嵌套了多个章节/诗句引用)
理想解标准
-
HTML需要在3层/嵌套中编写(第一层/div包含书名(例如创世纪,出埃及记,利未记),第二层/div包含章节,第三层/div包含诗句)
-
章节和经文必须对齐(例如,出埃及记2:7不应该写在创世纪第2章的菜单上)。
-
书名应该按照
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>
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
最后,请注意没有重复的章节/经文组合(例如创世记4:12不会出现第二次)。同样值得注意的是,章节和经文都需要按数字和升序排序。
与此类问题经常出现的情况一样,一旦将数据放入与所需输出非常相似的"形状"中,解决问题就变得容易得多。您的输出是一个嵌套的树结构,因此您的数据也应该是这样的。我们先做这个。这是你的数据:
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
Chapter 4
ID 1 - Verse 12
ID 1 - Verse 23-25
Chapter 6
ID 2 - Verse 17
Exodus
Chapter 2
ID 5 - Verse 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
,所以我们的最终结果是一个数组,而不是一个哈希。您可以对它调用to_h
使其再次成为哈希,但对于我们的目的来说,这是不必要的(迭代键值对数组的工作原理与迭代哈希相同)。
它看起来有点乱,但你可以看到结构在那里:诗句嵌套在章节中,章节嵌套在书中。现在我们只需要把它转换成HTML。
为了我们的残疾朋友,顺便说一句:正确用于嵌套树结构的HTML元素是
<ul>
或<ol>
。如果你有一些要求使用<div>
s代替你可以,但是为使用的作业用户使用正确的元素辅助设备会感谢你的。(已经写了很多文章所以我就不深入了,但作为开始,你可以用list-style-type: none;
隐藏子弹
我没有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)
结果为HTML片段:
<ul>
<li>
<a href="#">Genesis</a>
<ul>
<li>
<a href="#">Chapter 4</a>
<ul>
<li>
<a onclick="load(1)">ID 1: Verse 12</a>
</li>
<li>
<a onclick="load(1)">ID 1: Verse 23-25</a>
</li>
</ul>
</li>
<li>
<a href="#">Chapter 6</a>
<ul>
<li>
<a onclick="load(2)">ID 2: Verse 17</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Exodus</a>
<ul>
<li>
<a href="#">Chapter 2</a>
<ul>
<li>
<a onclick="load(5)">ID 5: Verse 7</a>
</li>
<li>
<a onclick="load(3)">ID 3: Verse 14-15</a>
</li>
</ul>
</li>
<li>
<a href="#">Chapter 12</a>
<ul>
<li>
<a onclick="load(4)">ID 4: Verse 16</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Leviticus</a>
<ul>
<li>
<a href="#">Chapter 11</a>
<ul>
<li>
<a onclick="load(2)">ID 2: Verse 19-21</a>
</li>
</ul>
</li>
<li>
<a href="#">Chapter 15</a>
<ul>
<li>
<a onclick="load(7)">ID 7: Verse 14-31</a>
</li>
</ul>
</li>
<li>
<a href="#">Chapter 19</a>
<ul>
<li>
<a onclick="load(7)">ID 7: Verse 11-12</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
最后,下面是它在repl上的作用。: https://repl.it/Duhm