Search code examples
clojurescript

Partial change of dictionary with clojurescript


I try to write this d3.js's code https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd with clojurescript because I want to use re-frame framework.

But I could not find any solution because it has the process of changing values in a dictionary such as the function #'collapse.

I have a problem. Can ClojureScript write changing values in a dictionary?

ex.

function remove_children (d) {
   if (d.children) {
     d.children = null;
   }
}

family_tree = {
  name: "John",
  children: [
     { name: "Jack",
       children: [
          { name: "Michel"}]},
     { name: "Emily",
       children: [
          { name: "Mike"}]}]
};

jack = family_tree.children[0]
remove_children(jack)
;; in clojurescript ... I have no solution ...

Solution

  • As mentioned above, you might run into issues later if you are trying to work with mutable data (for D3) and immutable data (for re-frame).

    When possible, use ClojureScript's own data structures and only pass JavaScript objects when iteracting with libraries, eg. you can use (clj->js my-map) to convert a CLJS map into a JS object.

    If you want to use JS interop to mutate JavaScript objects, that's also possible:

    (def family-tree
      (clj->js
        {
          "name" "John",
          "children" [
             { "name" "Jack",
               "children" [
                  { "name" "Michel"}]},
             { "name" "Emily",
               "children" [
                  { "name" "Mike"}]}]
        }))
    
    ;; Check if family-tree looks like expected:
    
    cljs.user=> family-tree
    #js {:name "John",
         :children #js [#js {:name "Jack",
                             :children #js [#js {:name "Michel"}]}
                        #js {:name "Emily",
                             :children #js [#js {:name "Mike"}]}]}
    
    ;; Define remove-children and Jack using interop:
    
    (defn remove-children [elem]
      (set! (.-children elem) nil))
    
    (def jack
      (-> family-tree .-children (get 0)))
    
    ;; Test them
    
    cljs.user=> (remove-children jack)
    nil
    
    cljs.user=> family-tree
    #js {:name "John",
         :children #js [#js {:name "Jack", :children nil} ;; <== Children were deleted
                        #js {:name "Emily",
                             :children #js [#js {:name "Mike"}]}]}