简体   繁体   English

Clojure:将集合转换为多棵树

[英]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来再次丢弃外部根映射,但这是可选的。

Edit for regex issues: 编辑正则表达式问题:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM