Search code examples
graphclojure

how to convert a nested map to a set of edges


I have a graph in nested maps which I would like to turn into a set of edges with node ids, node labels and edge labels:

#{{:edge/from 17592186325348 :edge/to 17592186325351 
:edge/label :A2 
:edge/from_node_label "a" :edge/to_node_label "l"}} 

Here, I have shown the desired format for the edge a->l that appears twice in the datastructure below. The data structure starts with a "source node". Children encode other targets. :edge_label is the label for the directed edge from the parent to the child.

I'm guessing that a recursive solution would be easiest?

{:db/id 17592186325353,
 :predicate/children [{:db/id 17592186325357,
                       :predicate/children [{:db/id 17592186325348,
                                             :predicate/children [{:db/id 17592186325351,
                                                                   :predicate/children [],
                                                                   :node_label "l",
                                                                   :edge_label :A1}],
                                             :node_label "a",
                                             :edge_label :A2}],
                       :node_label "m",
                       :edge_label :A2}
                      {:db/id 17592186325348,
                       :predicate/children [{:db/id 17592186325351,
                                             :predicate/children [],
                                             :node_label "l",
                                             :edge_label :A1}],
                       :node_label "a",
                       :edge_label :A1}],
 :node_label "e"}

It is a graph, because there is a cycle at a. However, for the purposes of visualization I'm abusing view-tree from rhizome: enter image description here


Solution

  • (map (partial zipmap [:edge/from :edge/to :edge/from_node_labe :edge/to_node_label :edge/label])
    (mapcat (fn [a] (map (fn [a b] [(:db/id a) (:db/id b) (:node_label a) (:node_label b)  (:edge_label b)]) (repeat a) (:predicate/children a))) 
    (remove (comp empty? :predicate/children) 
    (tree-seq :predicate/children :predicate/children your-original-data-structure))))
    

    gives:

    ({:edge/from 17592186325353,
      :edge/to 17592186325357,
      :edge/from_node_labe "e",
      :edge/to_node_label "m",
      :edge/label :A2}
     {:edge/from 17592186325353,
      :edge/to 17592186325348,
      :edge/from_node_labe "e",
      :edge/to_node_label "a",
      :edge/label :A1}
     {:edge/from 17592186325357,
      :edge/to 17592186325348,
      :edge/from_node_labe "m",
      :edge/to_node_label "a",
      :edge/label :A2}
     {:edge/from 17592186325348,
      :edge/to 17592186325351,
      :edge/from_node_labe "a",
      :edge/to_node_label "l",
      :edge/label :A1}
     {:edge/from 17592186325348,
      :edge/to 17592186325351,
      :edge/from_node_labe "a",
      :edge/to_node_label "l",
      :edge/label :A1})
    

    I am not sure if it is the easiest.