簡體   English   中英

Clojure:將集合轉換為多棵樹

[英]Clojure: transform collection into multiple trees

因此,我有一個用於注釋的數據庫表,並且學習了如何使用WITH RECURSIVE以樹的形式返回主題的所有注釋。 但是,由於它是SQL,因此只是作為列表返回。

當我執行查詢時,這些是我取回的結果(級別不是表上的列,它是由查詢在收集結果時計算的):

[
 {
  :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,
 }
]

我想弄清楚的是如何轉動多棵樹,這樣我最終得到如下結果:

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'  

使用此功能只會使我得到第一棵樹(根節點為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)})))

關於如何修改該功能或更改所傳遞的內容的任何想法或提示,以便最終獲得多棵樹?

您可以定義一個make-trees函數:

(defn make-trees [id coll] 
  (map 
    (fn [node] {:node node :children (make-trees (node :id) coll)})
    (filter #(= (% :parent_id) id) coll)))

這樣稱呼:

(make-trees nil YOUR_COLL)

如果您可以依靠:level條目,那么它可以用作與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)))

在這里,我在reduce的結果上使用vals來再次丟棄外部根映射,但這是可選的。

編輯正則表達式問題:

如果要在其上使用的實際數據實際上在:level具有UUID,則需要使用更合適的正則表達式。 上面將把十進制數字的任何部分都當作ID。 使用這些答案,我們可以改為收集:level字符串中的所有UUID。

我用一些隨機的UUID代替了您給出的數字來重新整理了示例數據 然后,使用以上鏈接中的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)))

獲得輸出

({: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 {}}}}}})

原來@coredump有正確的主意。 通過使頂級注釋具有其parent-id作為主題,那么我可以使用clojure.zip/zipper輕松構建一棵樹。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM