Clojure parse nested vectors












1














I am looking to transform a clojure tree structure into a map with its dependencies



For example, an input like:



[{:value "A"} 
[{:value "B"}
[{:value "C"} {:value "D"}]
[{:value "E"} [{:value "F"}]]]]


equivalent to:



:A
:B
:C
:D
:E
:F


output:



 {:A [:B :E] :B [:C :D] :C  :D  :E [:F] :F}


I have taken a look at tree-seq and zippers but can't figure it out!










share|improve this question






















  • I think there may be a misplaced bracket in your first input example, that structure doesn’t quite match the other two examples. Also the output map is missing a value for :F.
    – Taylor Wood
    Nov 19 at 6:49
















1














I am looking to transform a clojure tree structure into a map with its dependencies



For example, an input like:



[{:value "A"} 
[{:value "B"}
[{:value "C"} {:value "D"}]
[{:value "E"} [{:value "F"}]]]]


equivalent to:



:A
:B
:C
:D
:E
:F


output:



 {:A [:B :E] :B [:C :D] :C  :D  :E [:F] :F}


I have taken a look at tree-seq and zippers but can't figure it out!










share|improve this question






















  • I think there may be a misplaced bracket in your first input example, that structure doesn’t quite match the other two examples. Also the output map is missing a value for :F.
    – Taylor Wood
    Nov 19 at 6:49














1












1








1







I am looking to transform a clojure tree structure into a map with its dependencies



For example, an input like:



[{:value "A"} 
[{:value "B"}
[{:value "C"} {:value "D"}]
[{:value "E"} [{:value "F"}]]]]


equivalent to:



:A
:B
:C
:D
:E
:F


output:



 {:A [:B :E] :B [:C :D] :C  :D  :E [:F] :F}


I have taken a look at tree-seq and zippers but can't figure it out!










share|improve this question













I am looking to transform a clojure tree structure into a map with its dependencies



For example, an input like:



[{:value "A"} 
[{:value "B"}
[{:value "C"} {:value "D"}]
[{:value "E"} [{:value "F"}]]]]


equivalent to:



:A
:B
:C
:D
:E
:F


output:



 {:A [:B :E] :B [:C :D] :C  :D  :E [:F] :F}


I have taken a look at tree-seq and zippers but can't figure it out!







clojure tree zipper






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 19 at 0:02









Rahul S

61




61












  • I think there may be a misplaced bracket in your first input example, that structure doesn’t quite match the other two examples. Also the output map is missing a value for :F.
    – Taylor Wood
    Nov 19 at 6:49


















  • I think there may be a misplaced bracket in your first input example, that structure doesn’t quite match the other two examples. Also the output map is missing a value for :F.
    – Taylor Wood
    Nov 19 at 6:49
















I think there may be a misplaced bracket in your first input example, that structure doesn’t quite match the other two examples. Also the output map is missing a value for :F.
– Taylor Wood
Nov 19 at 6:49




I think there may be a misplaced bracket in your first input example, that structure doesn’t quite match the other two examples. Also the output map is missing a value for :F.
– Taylor Wood
Nov 19 at 6:49












2 Answers
2






active

oldest

votes


















1














Here's a way to build up the desired map while using a zipper to traverse the tree. First let's simplify the input tree to match your output format (maps of :value strings → keywords):



(def tree
[{:value "A"}
[{:value "B"} [{:value "C"} {:value "D"}]
{:value "E"} [{:value "F"}]]])

(def simpler-tree
(clojure.walk/postwalk
#(if (map? %) (keyword (:value %)) %)
tree))
;; [:A [:B [:C :D] :E [:F]]]


Then you can traverse the tree with loop/recur and clojure.zip/next, using two loop bindings: the current position in tree, and the map being built.



(loop [loc (z/vector-zip simpler-tree)
deps {}]
(if (z/end? loc)
deps ;; return map when end is reached
(recur
(z/next loc) ;; advance through tree
(if (z/branch? loc)
;; for (non-root) branches, add top-level key with direct descendants
(if-let [parent (some-> (z/prev loc) z/node)]
(assoc deps parent (filterv keyword? (z/children loc)))
deps)
;; otherwise add top-level key with no direct descendants
(assoc deps (z/node loc) )))))
=> {:A [:B :E], :B [:C :D], :C , :D , :E [:F], :F }





share|improve this answer

















  • 1




    I like this answer, but you've actually changed the structure of the example input, by removing the [ before the E node. I think you're probably right and OP mis-typed the input, because the proposed input format is really super-bizarre and what you've interpreted it as is reasonable, but it bears pointing out.
    – amalloy
    Nov 19 at 6:39










  • @amalloy thanks for pointing out, I meant to call this out and forgot. I inferred the “correct” tree from the question’s “equivalent to” and “output” samples when I noticed the discrepancy.
    – Taylor Wood
    Nov 19 at 6:46



















0














This is easy to do using the tupelo.forest library. I reformatted your source data to make it fit into the Hiccup syntax:



(dotest
(let [relationhip-data-hiccup [:A
[:B
[:C]
[:D]]
[:E
[:F]]]
expected-result {:A [:B :E]
:B [:C :D]
:C
:D
:E [:F]
:F } ]
(with-debug-hid
(with-forest (new-forest)
(let [root-hid (tf/add-tree-hiccup relationhip-data-hiccup)
result (apply glue (sorted-map)
(forv [hid (all-hids)]
(let [parent-tag (grab :tag (hid->node hid))
kid-tags (forv [kid-hid (hid->kids hid)]
(let [kid-tag (grab :tag (hid->node kid-hid))]
kid-tag))]
{parent-tag kid-tags})))]
(is= (format-paths (find-paths root-hid [:A]))
[[{:tag :A}
[{:tag :B} [{:tag :C}] [{:tag :D}]]
[{:tag :E} [{:tag :F}]]]])
(is= result expected-result ))))))


API docs are here. The project README (in progress) is here. A video from the 2017 Clojure Conj is here.



You can see the above live code in the project repo.






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53366714%2fclojure-parse-nested-vectors%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    Here's a way to build up the desired map while using a zipper to traverse the tree. First let's simplify the input tree to match your output format (maps of :value strings → keywords):



    (def tree
    [{:value "A"}
    [{:value "B"} [{:value "C"} {:value "D"}]
    {:value "E"} [{:value "F"}]]])

    (def simpler-tree
    (clojure.walk/postwalk
    #(if (map? %) (keyword (:value %)) %)
    tree))
    ;; [:A [:B [:C :D] :E [:F]]]


    Then you can traverse the tree with loop/recur and clojure.zip/next, using two loop bindings: the current position in tree, and the map being built.



    (loop [loc (z/vector-zip simpler-tree)
    deps {}]
    (if (z/end? loc)
    deps ;; return map when end is reached
    (recur
    (z/next loc) ;; advance through tree
    (if (z/branch? loc)
    ;; for (non-root) branches, add top-level key with direct descendants
    (if-let [parent (some-> (z/prev loc) z/node)]
    (assoc deps parent (filterv keyword? (z/children loc)))
    deps)
    ;; otherwise add top-level key with no direct descendants
    (assoc deps (z/node loc) )))))
    => {:A [:B :E], :B [:C :D], :C , :D , :E [:F], :F }





    share|improve this answer

















    • 1




      I like this answer, but you've actually changed the structure of the example input, by removing the [ before the E node. I think you're probably right and OP mis-typed the input, because the proposed input format is really super-bizarre and what you've interpreted it as is reasonable, but it bears pointing out.
      – amalloy
      Nov 19 at 6:39










    • @amalloy thanks for pointing out, I meant to call this out and forgot. I inferred the “correct” tree from the question’s “equivalent to” and “output” samples when I noticed the discrepancy.
      – Taylor Wood
      Nov 19 at 6:46
















    1














    Here's a way to build up the desired map while using a zipper to traverse the tree. First let's simplify the input tree to match your output format (maps of :value strings → keywords):



    (def tree
    [{:value "A"}
    [{:value "B"} [{:value "C"} {:value "D"}]
    {:value "E"} [{:value "F"}]]])

    (def simpler-tree
    (clojure.walk/postwalk
    #(if (map? %) (keyword (:value %)) %)
    tree))
    ;; [:A [:B [:C :D] :E [:F]]]


    Then you can traverse the tree with loop/recur and clojure.zip/next, using two loop bindings: the current position in tree, and the map being built.



    (loop [loc (z/vector-zip simpler-tree)
    deps {}]
    (if (z/end? loc)
    deps ;; return map when end is reached
    (recur
    (z/next loc) ;; advance through tree
    (if (z/branch? loc)
    ;; for (non-root) branches, add top-level key with direct descendants
    (if-let [parent (some-> (z/prev loc) z/node)]
    (assoc deps parent (filterv keyword? (z/children loc)))
    deps)
    ;; otherwise add top-level key with no direct descendants
    (assoc deps (z/node loc) )))))
    => {:A [:B :E], :B [:C :D], :C , :D , :E [:F], :F }





    share|improve this answer

















    • 1




      I like this answer, but you've actually changed the structure of the example input, by removing the [ before the E node. I think you're probably right and OP mis-typed the input, because the proposed input format is really super-bizarre and what you've interpreted it as is reasonable, but it bears pointing out.
      – amalloy
      Nov 19 at 6:39










    • @amalloy thanks for pointing out, I meant to call this out and forgot. I inferred the “correct” tree from the question’s “equivalent to” and “output” samples when I noticed the discrepancy.
      – Taylor Wood
      Nov 19 at 6:46














    1












    1








    1






    Here's a way to build up the desired map while using a zipper to traverse the tree. First let's simplify the input tree to match your output format (maps of :value strings → keywords):



    (def tree
    [{:value "A"}
    [{:value "B"} [{:value "C"} {:value "D"}]
    {:value "E"} [{:value "F"}]]])

    (def simpler-tree
    (clojure.walk/postwalk
    #(if (map? %) (keyword (:value %)) %)
    tree))
    ;; [:A [:B [:C :D] :E [:F]]]


    Then you can traverse the tree with loop/recur and clojure.zip/next, using two loop bindings: the current position in tree, and the map being built.



    (loop [loc (z/vector-zip simpler-tree)
    deps {}]
    (if (z/end? loc)
    deps ;; return map when end is reached
    (recur
    (z/next loc) ;; advance through tree
    (if (z/branch? loc)
    ;; for (non-root) branches, add top-level key with direct descendants
    (if-let [parent (some-> (z/prev loc) z/node)]
    (assoc deps parent (filterv keyword? (z/children loc)))
    deps)
    ;; otherwise add top-level key with no direct descendants
    (assoc deps (z/node loc) )))))
    => {:A [:B :E], :B [:C :D], :C , :D , :E [:F], :F }





    share|improve this answer












    Here's a way to build up the desired map while using a zipper to traverse the tree. First let's simplify the input tree to match your output format (maps of :value strings → keywords):



    (def tree
    [{:value "A"}
    [{:value "B"} [{:value "C"} {:value "D"}]
    {:value "E"} [{:value "F"}]]])

    (def simpler-tree
    (clojure.walk/postwalk
    #(if (map? %) (keyword (:value %)) %)
    tree))
    ;; [:A [:B [:C :D] :E [:F]]]


    Then you can traverse the tree with loop/recur and clojure.zip/next, using two loop bindings: the current position in tree, and the map being built.



    (loop [loc (z/vector-zip simpler-tree)
    deps {}]
    (if (z/end? loc)
    deps ;; return map when end is reached
    (recur
    (z/next loc) ;; advance through tree
    (if (z/branch? loc)
    ;; for (non-root) branches, add top-level key with direct descendants
    (if-let [parent (some-> (z/prev loc) z/node)]
    (assoc deps parent (filterv keyword? (z/children loc)))
    deps)
    ;; otherwise add top-level key with no direct descendants
    (assoc deps (z/node loc) )))))
    => {:A [:B :E], :B [:C :D], :C , :D , :E [:F], :F }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 19 at 5:44









    Taylor Wood

    9,8581624




    9,8581624








    • 1




      I like this answer, but you've actually changed the structure of the example input, by removing the [ before the E node. I think you're probably right and OP mis-typed the input, because the proposed input format is really super-bizarre and what you've interpreted it as is reasonable, but it bears pointing out.
      – amalloy
      Nov 19 at 6:39










    • @amalloy thanks for pointing out, I meant to call this out and forgot. I inferred the “correct” tree from the question’s “equivalent to” and “output” samples when I noticed the discrepancy.
      – Taylor Wood
      Nov 19 at 6:46














    • 1




      I like this answer, but you've actually changed the structure of the example input, by removing the [ before the E node. I think you're probably right and OP mis-typed the input, because the proposed input format is really super-bizarre and what you've interpreted it as is reasonable, but it bears pointing out.
      – amalloy
      Nov 19 at 6:39










    • @amalloy thanks for pointing out, I meant to call this out and forgot. I inferred the “correct” tree from the question’s “equivalent to” and “output” samples when I noticed the discrepancy.
      – Taylor Wood
      Nov 19 at 6:46








    1




    1




    I like this answer, but you've actually changed the structure of the example input, by removing the [ before the E node. I think you're probably right and OP mis-typed the input, because the proposed input format is really super-bizarre and what you've interpreted it as is reasonable, but it bears pointing out.
    – amalloy
    Nov 19 at 6:39




    I like this answer, but you've actually changed the structure of the example input, by removing the [ before the E node. I think you're probably right and OP mis-typed the input, because the proposed input format is really super-bizarre and what you've interpreted it as is reasonable, but it bears pointing out.
    – amalloy
    Nov 19 at 6:39












    @amalloy thanks for pointing out, I meant to call this out and forgot. I inferred the “correct” tree from the question’s “equivalent to” and “output” samples when I noticed the discrepancy.
    – Taylor Wood
    Nov 19 at 6:46




    @amalloy thanks for pointing out, I meant to call this out and forgot. I inferred the “correct” tree from the question’s “equivalent to” and “output” samples when I noticed the discrepancy.
    – Taylor Wood
    Nov 19 at 6:46













    0














    This is easy to do using the tupelo.forest library. I reformatted your source data to make it fit into the Hiccup syntax:



    (dotest
    (let [relationhip-data-hiccup [:A
    [:B
    [:C]
    [:D]]
    [:E
    [:F]]]
    expected-result {:A [:B :E]
    :B [:C :D]
    :C
    :D
    :E [:F]
    :F } ]
    (with-debug-hid
    (with-forest (new-forest)
    (let [root-hid (tf/add-tree-hiccup relationhip-data-hiccup)
    result (apply glue (sorted-map)
    (forv [hid (all-hids)]
    (let [parent-tag (grab :tag (hid->node hid))
    kid-tags (forv [kid-hid (hid->kids hid)]
    (let [kid-tag (grab :tag (hid->node kid-hid))]
    kid-tag))]
    {parent-tag kid-tags})))]
    (is= (format-paths (find-paths root-hid [:A]))
    [[{:tag :A}
    [{:tag :B} [{:tag :C}] [{:tag :D}]]
    [{:tag :E} [{:tag :F}]]]])
    (is= result expected-result ))))))


    API docs are here. The project README (in progress) is here. A video from the 2017 Clojure Conj is here.



    You can see the above live code in the project repo.






    share|improve this answer


























      0














      This is easy to do using the tupelo.forest library. I reformatted your source data to make it fit into the Hiccup syntax:



      (dotest
      (let [relationhip-data-hiccup [:A
      [:B
      [:C]
      [:D]]
      [:E
      [:F]]]
      expected-result {:A [:B :E]
      :B [:C :D]
      :C
      :D
      :E [:F]
      :F } ]
      (with-debug-hid
      (with-forest (new-forest)
      (let [root-hid (tf/add-tree-hiccup relationhip-data-hiccup)
      result (apply glue (sorted-map)
      (forv [hid (all-hids)]
      (let [parent-tag (grab :tag (hid->node hid))
      kid-tags (forv [kid-hid (hid->kids hid)]
      (let [kid-tag (grab :tag (hid->node kid-hid))]
      kid-tag))]
      {parent-tag kid-tags})))]
      (is= (format-paths (find-paths root-hid [:A]))
      [[{:tag :A}
      [{:tag :B} [{:tag :C}] [{:tag :D}]]
      [{:tag :E} [{:tag :F}]]]])
      (is= result expected-result ))))))


      API docs are here. The project README (in progress) is here. A video from the 2017 Clojure Conj is here.



      You can see the above live code in the project repo.






      share|improve this answer
























        0












        0








        0






        This is easy to do using the tupelo.forest library. I reformatted your source data to make it fit into the Hiccup syntax:



        (dotest
        (let [relationhip-data-hiccup [:A
        [:B
        [:C]
        [:D]]
        [:E
        [:F]]]
        expected-result {:A [:B :E]
        :B [:C :D]
        :C
        :D
        :E [:F]
        :F } ]
        (with-debug-hid
        (with-forest (new-forest)
        (let [root-hid (tf/add-tree-hiccup relationhip-data-hiccup)
        result (apply glue (sorted-map)
        (forv [hid (all-hids)]
        (let [parent-tag (grab :tag (hid->node hid))
        kid-tags (forv [kid-hid (hid->kids hid)]
        (let [kid-tag (grab :tag (hid->node kid-hid))]
        kid-tag))]
        {parent-tag kid-tags})))]
        (is= (format-paths (find-paths root-hid [:A]))
        [[{:tag :A}
        [{:tag :B} [{:tag :C}] [{:tag :D}]]
        [{:tag :E} [{:tag :F}]]]])
        (is= result expected-result ))))))


        API docs are here. The project README (in progress) is here. A video from the 2017 Clojure Conj is here.



        You can see the above live code in the project repo.






        share|improve this answer












        This is easy to do using the tupelo.forest library. I reformatted your source data to make it fit into the Hiccup syntax:



        (dotest
        (let [relationhip-data-hiccup [:A
        [:B
        [:C]
        [:D]]
        [:E
        [:F]]]
        expected-result {:A [:B :E]
        :B [:C :D]
        :C
        :D
        :E [:F]
        :F } ]
        (with-debug-hid
        (with-forest (new-forest)
        (let [root-hid (tf/add-tree-hiccup relationhip-data-hiccup)
        result (apply glue (sorted-map)
        (forv [hid (all-hids)]
        (let [parent-tag (grab :tag (hid->node hid))
        kid-tags (forv [kid-hid (hid->kids hid)]
        (let [kid-tag (grab :tag (hid->node kid-hid))]
        kid-tag))]
        {parent-tag kid-tags})))]
        (is= (format-paths (find-paths root-hid [:A]))
        [[{:tag :A}
        [{:tag :B} [{:tag :C}] [{:tag :D}]]
        [{:tag :E} [{:tag :F}]]]])
        (is= result expected-result ))))))


        API docs are here. The project README (in progress) is here. A video from the 2017 Clojure Conj is here.



        You can see the above live code in the project repo.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 19 at 1:53









        Alan Thompson

        13k22533




        13k22533






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53366714%2fclojure-parse-nested-vectors%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            What visual should I use to simply compare current year value vs last year in Power BI desktop

            Alexandru Averescu

            Trompette piccolo