[英]Clojure: transform collection into multiple trees
So I've got a database table for comments, and I learned how to use WITH RECURSIVE
to return all the comments for a topic, as a tree. 因此,我有一个用于注释的数据库表,并且学习了如何使用
WITH RECURSIVE
以树的形式返回主题的所有注释。 However, because it's SQL, it's just returned as a list. 但是,由于它是SQL,因此只是作为列表返回。
When I execute my query, these are the results I get back ( level is not a column on the table, it's calculated by the query as it gathers results ): 当我执行查询时,这些是我取回的结果(级别不是表上的列,它是由查询在收集结果时计算的):
[
{
:id "1"
:parent_id nil,
:content "This is another top-level comment",
:level "1",
:rating 0,
}
{
:id "2"
:parent_id "1",
:content "What a comment!",
:level "1 -> 2",
:rating 0,
}
{
:id "4"
:parent_id "2",
:content "Trying to see how trees work",
:level "1 -> 2 -> 4",
:rating 0,
}
{
:id "3"
:parent_id "2",
:content "No idea how this will turn out",
:level "1 -> 2 -> 3",
:rating 0,
}
{
:id "5"
:parent_id nil,
:content "This is a top-level comment",
:level "5",
:rating 0,
}
{
:id "9"
:parent_id "5",
:content "This is yet another testing comment",
:level "5 -> 9",
:rating 0,
}
{
:id "8"
:parent_id "7",
:content "It sure is!",
:level "5 -> 7 -> 8",
:rating 0,
}
{
:id "7"
:parent_id "5",
:content "This!",
:level "5 -> 7",
:rating 0,
}
{
:id "6"
:parent_id "5",
:content "Hey look at me",
:level "5 -> 6",
:rating 0,
}
]
What I'd like to figure out is how to turn multiple trees, so that I end up with something like so: 我想弄清楚的是如何转动多棵树,这样我最终得到如下结果:
1 'This is another top-level comment'
↳ 2 'What a comment!'
↳ 4 'Trying to see how trees work'
↳ 3 'No idea how this will turn out'
5 'This is a top-level comment'
↳ 9 'This is yet another testing comment'
↳ 7 'This!'
↳ 8 'It sure is!'
↳ 6 'Hey look at me'
Using this function only gets me the first tree ( the one with root node of id 1 ): 使用此功能只会使我得到第一棵树(根节点为id 1的树):
(defn make-tree
([coll] (let [root (first (remove :parent coll))]
{:node root :children (make-tree root coll)}))
([root coll]
(for [x coll :when (= (:parent_id x) (:id root))]
{:node x :children (make-tree x coll)})))
Any ideas or hints on how I could either modify that function, or change what I'm passing in so that I end up with multiple trees? 关于如何修改该功能或更改所传递的内容的任何想法或提示,以便最终获得多棵树?
you can define a make-trees function: 您可以定义一个make-trees函数:
(defn make-trees [id coll]
(map
(fn [node] {:node node :children (make-trees (node :id) coll)})
(filter #(= (% :parent_id) id) coll)))
called like this: 这样称呼:
(make-trees nil YOUR_COLL)
If you can rely on the :level
entry, that can work OK as a source of key sequences to use with assoc-in
. 如果您可以依靠
:level
条目,那么它可以用作与assoc-in
一起使用的键序列的来源。 You can then do the approach @coredump mentioned with a dedicated root node pretty simply using reduce
and a small lambda built on assoc-in
: 然后,您可以使用专用的根节点执行方法@coredump,只需使用
reduce
和在assoc-in
上构建的小lambda即可:
(defn- key-seq [comment]
(->> comment
:level
(re-seq (re-pattern "\\d+"))
(interpose :children)))
(defn list->forest [comments]
(vals (reduce (fn [root comment]
(assoc-in root (key-seq comment) {:node comment :children {}}))
{}
comments)))
Here I use vals
on the result of the reduce
to discard the outer root map again, but that's kinda optional. 在这里,我在
reduce
的结果上使用vals
来再次丢弃外部根映射,但这是可选的。
If the real data you want to use this on actually has UUIDs in the :level
then we'll need to use a more appropriate regex. 如果要在其上使用的实际数据实际上在
:level
具有UUID,则需要使用更合适的正则表达式。 The above will treat any section of decimal digits as an ID. 上面将把十进制数字的任何部分都当作ID。 Using these answers we can collect all the UUIDs in the
:level
string instead. 使用这些答案,我们可以改为收集
:level
字符串中的所有UUID。
I reworked your example data with some random UUIDs in place of the numbers you gave. 我用一些随机的UUID代替了您给出的数字来重新整理了示例数据 。 Using Gajus Kuizinas' regex from the above link I then did these redefinitions:
然后,使用以上链接中的Gajus Kuizinas的正则表达式进行了以下重新定义:
(ns comment-forest
(:require [clojure.walk :refer [postwalk]]
[clojure.pprint :refer [pprint]])
(:import java.util.UUID))
(defn- key-seq [comment]
(->> comment
:level
(re-seq (re-pattern "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[89aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}"))
(map #(UUID/fromString %))
(interpose :children)))
;;This is just to print the trees with less unnecessary detail
(defn- prune [value]
(if
(or
(not (map? value))
(every? (partial contains? value) [:node :children])
(every? #(= UUID (type %)) (keys value)))
value
(select-keys value [:id :content])))
(pprint (map (partial postwalk prune) (list->forest querylist)))
to get output 获得输出
({:node
{:content "This is a top-level comment",
:id "ee9a2671-b47e-40ef-994f-a7b0fa81d717"},
:children
{#uuid "f28a159c-de66-4712-9cb8-e1841afeebf6"
{:node
{:content "Hey look at me",
:id "f28a159c-de66-4712-9cb8-e1841afeebf6"},
:children {}},
#uuid "d3fccc58-5e59-486d-b784-c54f0e4698b1"
{:node
{:content "This!", :id "d3fccc58-5e59-486d-b784-c54f0e4698b1"},
:children
{#uuid "e6387f7d-4f29-42c9-a386-7f799341f48f"
{:node
{:content "It sure is!",
:id "e6387f7d-4f29-42c9-a386-7f799341f48f"},
:children {}}}},
#uuid "3de27950-7340-49d1-a28e-54ad2e4ea0f1"
{:node
{:content "This is yet another testing comment",
:id "3de27950-7340-49d1-a28e-54ad2e4ea0f1"},
:children {}}}}
{:node
{:content "This is another top-level comment",
:id "fdc8a8b9-19c7-4fad-963d-2c2ca0bcbe8a"},
:children
{#uuid "b17bc5b8-9968-48ce-8ff3-83c8123cd327"
{:node
{:content "What a comment!",
:id "b17bc5b8-9968-48ce-8ff3-83c8123cd327"},
:children
{#uuid "1cee5390-e810-49b7-ad10-098bfbe03ab2"
{:node
{:content "No idea how this will turn out",
:id "1cee5390-e810-49b7-ad10-098bfbe03ab2"},
:children {}}}}}})
Turns out @coredump had the right idea. 原来@coredump有正确的主意。 By having the top-level comments have their parent-id be the topic, then I can just use clojure.zip/zipper to build a tree pretty easily.
通过使顶级注释具有其parent-id作为主题,那么我可以使用clojure.zip/zipper轻松构建一棵树。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.