Clojure - 转换嵌套映射

时间:2016-07-12 13:27:15

标签: clojure

我想从嵌套地图中创建一个mermaid图表,如此

{"a" {"b" {"c" nil
           "d" nil}}
 "e" {"c" nil
      "d" {"h" {"i" nil
                "j" nil}}}}

diagram

我认为首先应该将嵌套地图转换为此表单。那应该很容易。

[{:out-path "a" :out-name "a"
  :in-path  "a-b" :in-name "b"}
 {:out-path "a-b" :out-name "b"
  :in-path  "a-b-c" :in-name "c"}
 {:out-path "a-b" :out-name "b"
  :in-path  "a-b-d" :in-name "d"}
 {:out-path "e" :out-name "e"
  :in-path  "e-f" :in-name "f"}
 {:out-path "e" :out-name "e"
  :in-path  "e-c" :in-name "c"}
 {:out-path "e" :out-name "e"
  :in-path  "e-d" :in-name "d"}
 {:out-path "e-d" :out-name "d"
  :in-path  "e-d-h" :in-name "h"}
 {:out-path "e-d-h" :out-name "h"
  :in-path  "e-d-h-i" :in-name "i"}
 {:out-path "e-d-h" :out-name "h"
  :in-path  "e-d-h-j" :in-name "j"}]

编辑:

这就是我创造的。但我完全不知道如何添加路径到结果图。

(defn myfunc [m]
  (loop [in m out []]
    (let [[[k v] & ts] (seq in)]
      (if (keyword? k)
        (cond
          (map? v)
          (recur (concat v ts)
                 (reduce (fn [o k2]
                           (conj o {:out-name (name k)
                                    :in-name  (name k2)}))
                         out (keys v)))
          (nil? v)
          (recur (concat v ts) out))
        out))))

1 个答案:

答案 0 :(得分:1)

据美人鱼文档我可以看到,绘制图形就足以生成" x - > y"形式的所有节点。对

我们可以通过一些简单的递归函数来做到这一点(我相信图中没有那么多级别来担心堆栈溢出):

(defn map->mermaid [items-map]
  (if (seq items-map)
    (mapcat (fn [[k v]] (concat 
                          (map (partial str k "-->") (keys v))
                          (map->mermaid v)))
      items-map)))

在repl中:

user>
(map->mermaid {"a" {"b" {"c" nil
                         "d" nil}}
               "e" {"c" nil
                    "d" {"h" {"i" nil
                              "j" nil}}}})

;; ("a-->b" "b-->c" "b-->d" "e-->c" "e-->d" "d-->h" "h-->i" "h-->j")

所以现在你只需要像这样制作一个图表:

(defn create-graph [items-map]
  (str "graph LR"
       \newline
       (clojure.string/join \newline (map->mermaid items-map))
       \newline))

<强>更新

你可以使用相同的策略进行实际的地图转换,只需将当前路径传递给map->mermaid

(defn make-result-node [path name child-name]
  {:out-path path
   :out-name name
   :in-path (str path "-" child-name)
   :in-name child-name})

(defn map->mermaid
  ([items-map] (map->mermaid "" items-map))
  ([path items-map]
   (if (seq items-map)
     (mapcat (fn [[k v]]
               (let [new-path (if (seq path) (str path "-" k) k)]
                 (concat (map (partial make-result-node new-path k)
                              (keys v))
                         (map->mermaid new-path v))))
             items-map))))

在repl中:

user> 
(map->mermaid {"a" {"b" {"c" nil
                         "d" nil}}
               "e" {"c" nil
                    "d" {"h" {"i" nil
                              "j" nil}}}})

;; ({:out-path "a", :out-name "a", :in-path "a-b", :in-name "b"} 
;;  {:out-path "a-b", :out-name "b", :in-path "a-b-c", :in-name "c"} 
;;  {:out-path "a-b", :out-name "b", :in-path "a-b-d", :in-name "d"} 
;;  {:out-path "e", :out-name "e", :in-path "e-c", :in-name "c"} 
;;  {:out-path "e", :out-name "e", :in-path "e-d", :in-name "d"} 
;;  {:out-path "e-d", :out-name "d", :in-path "e-d-h", :in-name "h"} 
;;  {:out-path "e-d-h", :out-name "h", :in-path "e-d-h-i", :in-name "i"} 
;;  {:out-path "e-d-h", :out-name "h", :in-path "e-d-h-j", :in-name "j"})