在 Clojure 中从地图构建嵌套向量
Building nested vector from map in Clojure
我有一组 URL,其中一些 URL 有间接引用(作为向量)。任何没有间接引用的 URL 只有 nil
。我从以下测试地图开始:
{"URL 1" nil,
"URL 2" ["indirect 1" "indirect 2"]}
我正在使用 hiccup 构建一个 HTML 报告,所以我想要这个输出:
[:div "Imports: "
[:ul
[:li "URL 1"]
[:li "URL 2"]
[:ul
[:li "indirect 1"]
[:li "indirect 2"]
[:li "indirect 3"]]]]
我 运行 遇到一些问题,当 URL 没有间接引用时返回 nil。我当前的代码如下所示:
(defn list-imports
[imports]
(if-not (nil? imports)
[:div "Imports: "
[:ul
(for [direct (keys imports)]
[[:li direct]
(if-let [indirects (get imports direct)]
[:ul
(for [indirect indirects]
[:li indirect])]
[:span])])]]
[:div "Imports: none" [:br] [:br]]))
问题是,它正在返回这个...
[:div
"Imports: "
[:ul
([[:li "URL 1"] [:span]]
[[:li "URL 2"] [:ul ([:li "indirect 1"] [:li "indirect 2"])]])]]
我不得不添加一个 [:span]
标签,因为当间接导入是 nil
时,我真的不想在那里...但除此之外,它会放入 nil
那里。
另一个问题是它最终包含在 ()
和一个额外的向量中,因为我在 for 语句中做了很多事情。当我尝试用 hiccup 转换它时,我得到 [:li "URL 1"] is not a valid element name.
这可能是构建打嗝标签的一个棘手方面。有时将问题分解成更小的部分会有所帮助。
(defn list-indirects
[indirects]
(when (seq indirects)
[(into [:ul] (mapv (fn [i] [:li i]) indirects))]))
(defn list-imports
[imports]
(if (some? imports)
[:div "Imports: "
(into [:ul]
(for [[url indirects] imports]
(into [:li url] (list-indirects indirects))))]
[:div "Imports: none" [:br] [:br]]))
这些函数应该会为您提供所需的输出。
(list-imports {"URL 1" nil
"URL 2" ["indirect 1" "indirect 2"]})
=>
[:div "Imports: "
[:ul
[:li "URL 1"]
[:li "URL 2"
[:ul
[:li "indirect 1"]
[:li "indirect 2"]]]]]
此输出与您的预期输出略有不同,但我认为它更接近您实际想要的,即示例中的 [:li "URL 2"]
标记应 contain "indirects" 的 :ul
有效 HTML.
另一件需要注意的事情是,如果这些项目的顺序很重要,地图可能不会按照您期望的方式排序,尤其是当您超过一定数量的键。当你遍历地图建造打嗝时,有可能"URL 2"
先于"URL 1"
。您可以通过使用元组向量或排序映射来解决此问题。
我有一组 URL,其中一些 URL 有间接引用(作为向量)。任何没有间接引用的 URL 只有 nil
。我从以下测试地图开始:
{"URL 1" nil,
"URL 2" ["indirect 1" "indirect 2"]}
我正在使用 hiccup 构建一个 HTML 报告,所以我想要这个输出:
[:div "Imports: "
[:ul
[:li "URL 1"]
[:li "URL 2"]
[:ul
[:li "indirect 1"]
[:li "indirect 2"]
[:li "indirect 3"]]]]
我 运行 遇到一些问题,当 URL 没有间接引用时返回 nil。我当前的代码如下所示:
(defn list-imports
[imports]
(if-not (nil? imports)
[:div "Imports: "
[:ul
(for [direct (keys imports)]
[[:li direct]
(if-let [indirects (get imports direct)]
[:ul
(for [indirect indirects]
[:li indirect])]
[:span])])]]
[:div "Imports: none" [:br] [:br]]))
问题是,它正在返回这个...
[:div
"Imports: "
[:ul
([[:li "URL 1"] [:span]]
[[:li "URL 2"] [:ul ([:li "indirect 1"] [:li "indirect 2"])]])]]
我不得不添加一个 [:span]
标签,因为当间接导入是 nil
时,我真的不想在那里...但除此之外,它会放入 nil
那里。
另一个问题是它最终包含在 ()
和一个额外的向量中,因为我在 for 语句中做了很多事情。当我尝试用 hiccup 转换它时,我得到 [:li "URL 1"] is not a valid element name.
这可能是构建打嗝标签的一个棘手方面。有时将问题分解成更小的部分会有所帮助。
(defn list-indirects
[indirects]
(when (seq indirects)
[(into [:ul] (mapv (fn [i] [:li i]) indirects))]))
(defn list-imports
[imports]
(if (some? imports)
[:div "Imports: "
(into [:ul]
(for [[url indirects] imports]
(into [:li url] (list-indirects indirects))))]
[:div "Imports: none" [:br] [:br]]))
这些函数应该会为您提供所需的输出。
(list-imports {"URL 1" nil
"URL 2" ["indirect 1" "indirect 2"]})
=>
[:div "Imports: "
[:ul
[:li "URL 1"]
[:li "URL 2"
[:ul
[:li "indirect 1"]
[:li "indirect 2"]]]]]
此输出与您的预期输出略有不同,但我认为它更接近您实际想要的,即示例中的 [:li "URL 2"]
标记应 contain "indirects" 的 :ul
有效 HTML.
另一件需要注意的事情是,如果这些项目的顺序很重要,地图可能不会按照您期望的方式排序,尤其是当您超过一定数量的键。当你遍历地图建造打嗝时,有可能"URL 2"
先于"URL 1"
。您可以通过使用元组向量或排序映射来解决此问题。