从 Parent/Child 关系创建嵌套映射

Create Nested Map From Parent/Child Relationships

给定一组 parent/child 关系,我该如何构建嵌套地图来显示此结构?

比如我要怎么转这个数据:

({"child"  "1.1",
  "parent" "1"}
 {"child"  "1.2",
  "parent" "1"}
 {"child"  "1.3",
  "parent" "1"}
 {"child"  "1.4",
  "parent" "1"}
 {"child"  "1.1.1",
  "parent" "1.1"}
 {"child"  "1.1.2",
  "parent" "1.1"}
 {"child"  "2.1",
  "parent" "2"}
 {"child"  "2.2",
  "parent" "2"}
 {"child"  "2.3",
  "parent" "2"})

进入这个:

{"1" {"1.1" {"1.1.1" {}
             "1.1.2" {}}
      "1.2" {}
      "1.3" {}
      "1.4" {}}
 "2" {"2.1" {}
      "2.2" {}
      "2.3" {}}}

我正在处理的实际数据是:

({"child" "94bf72cb-01cd-4430-b669-b2e954b5639b",
  "parent" "6378daf6-3b7f-4cf4-8156-a50cf5f7b6ef"}
 {"child" "8c9c57a0-d20d-4474-9afb-c9d17df83a91",
  "parent" "6378daf6-3b7f-4cf4-8156-a50cf5f7b6ef"}
 {"child" "73d2a203-e3c1-4d2f-aaf8-a9f2e870792b",
  "parent" "6378daf6-3b7f-4cf4-8156-a50cf5f7b6ef"}
 {"child" "669fe949-057f-43c0-af7b-ff39594a183d",
  "parent" "6378daf6-3b7f-4cf4-8156-a50cf5f7b6ef"}
 {"child" "1ceff8fe-a0a8-46ad-81a8-d13fb837aaf6",
  "parent" "94bf72cb-01cd-4430-b669-b2e954b5639b"}
 {"child" "ba96a425-a3f0-4ce5-8c19-6ea9add14013",
  "parent" "94bf72cb-01cd-4430-b669-b2e954b5639b"}
 {"child" "160f34ac-68b9-4c8e-930b-1ab6df895df4",
  "parent" "1ce9b863-5681-4660-85e7-fbd0cc184aed"}
 {"child" "58825b50-23bc-4204-8f8d-c9a9d3ac8beb",
  "parent" "1ce9b863-5681-4660-85e7-fbd0cc184aed"}
 {"child" "4b1763f9-8380-4507-9a8f-5c86878e49a9",
  "parent" "1ce9b863-5681-4660-85e7-fbd0cc184aed"})

您可以将子父关系转换为邻接表,然后遍历无环有向图以创建嵌套映射。

(defn adjacency-list [coll]
  (reduce (fn [r {p "parent" c "child"}]
            (-> r
              (update-in [:counts p] #(or % 0))
              (update-in [:counts c] #(if % (inc %) 1))
              (update-in [:adjacency p] #(if % (conj % c) [c]))))
          {}
          coll))

(defn root-nodes [counts]
  (mapv key
        (filter (comp zero? val) counts)))

(defn traverse [m al roots]
  (reduce (fn [r k]
            (assoc r k
                   (if-let [v (get al k)]
                     (traverse {} al v)
                     {})))
          m
          roots))

(def data '({"child" "1.1", "parent" "1"} {"child" "1.2", "parent" "1"} {"child" "1.3", "parent" "1"} {"child" "1.4", "parent" "1"} {"child" "1.1.1", "parent" "1.1"} {"child" "1.1.2", "parent" "1.1"} {"child" "2.1", "parent" "2"} {"child" "2.2", "parent" "2"} {"child" "2.3", "parent" "2"}))

(let [{:keys [counts adjacency]} (adjacency-list data)]
  (clojure.pprint/pprint (traverse {} adjacency (root-nodes counts))))

;=> {"2" {"2.3" {}, "2.2" {}, "2.1" {}},
;    "1" {"1.4" {}, "1.3" {}, "1.2" {}, "1.1" {"1.1.2" {}, "1.1.1" {}}}}