哈希数组-从哈希值中嵌套HTML菜单,无需写入重复项



我有一个相对较大的散列,其中所有键的值都是散列数组(参见下面的示例布局)。我花了4.5个小时试图为我的Rails应用程序编写HTML,我真的要哭了,因为我一直在兜圈子,没有任何真正的东西可以展示!

任何帮助都是非常感谢提前感谢您的时间!

遇到的具体问题

  1. 章节/诗句出现在不对齐的书籍中。
  2. 我也不能删除重复的条目,所以'Chapter 4',例如,出现多次(而不是出现一次,下面嵌套了多个章节/诗句引用)

理想解标准

  1. HTML需要在3层/嵌套中编写(第一层/div包含书名(例如创世纪,出埃及记,利未记),第二层/div包含章节,第三层/div包含诗句)

  2. 章节和经文必须对齐(例如,出埃及记2:7不应该写在创世纪第2章的菜单上)。

  3. 书名应该按照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

最新更新