Fork me on GitHub
#specter
<
2017-12-03
>
eoliphant20:12:57

hi, i have a quick question. I want to recursively search for an element in a data-structure (maps, that have lists, that have maps, etc), then do a setval before/after the element i found so I have this test data structure

#:designer{:cur-form-def #:form{:id #uuid"4979e704-5047-48cf-bfe3-fdf9ca58bae8",
                                :name "New Form",
                                :description "Description",
                                :version 1,
                                :groups [#:group{:id #uuid"d865ab2f-d114-4853-9a37-592a2c941152",
                                                 :title "Page 1",
                                                 :type :page,
                                                 :layout :vertical,
                                                 :subgroups [#:group{:id #uuid"237a7f3e-702e-4b63-8b4e-5df4da205e22",
                                                                     :name "row1",
                                                                     :type :row,
                                                                     :layout :horizontal,
                                                                     :fields [#:field{:id #uuid"b9a2c9ea-2647-48d3-ab26-4c2a115d7b6f",
                                                                                      :name :newfield,
                                                                                      :label "New Fuekd",
                                                                                      :type :text}]}]}]}}
And I want to find the :newfield by its id and jam a new map in the containing vector after :newfield so I do the following:
(sp/setval [:designer/cur-form-def
            :form/groups
            sp/ALL
            (sp/walker #(= (:field/id %) #uuid "b9a2c9ea-2647-48d3-ab26-4c2a115d7b6f"))
            sp/AFTER-ELEM
            ] [{:test :newfield}] appdb)
which gives me this:
#:designer{:cur-form-def #:form{:id #uuid"4979e704-5047-48cf-bfe3-fdf9ca58bae8",
                                :name "New Form",
                                :description "Description",
                                :version 1,
                                :groups [#:group{:id #uuid"d865ab2f-d114-4853-9a37-592a2c941152",
                                                 :title "Page 1",
                                                 :type :page,
                                                 :layout :vertical,
                                                 :subgroups [#:group{:id #uuid"237a7f3e-702e-4b63-8b4e-5df4da205e22",
                                                                     :name "row1",
                                                                     :type :row,
                                                                     :layout :horizontal,
                                                                     :fields [([:field/id
                                                                                #uuid"b9a2c9ea-2647-48d3-ab26-4c2a115d7b6f"]
                                                                               [:field/name :newfield]
                                                                               [:field/label "New Fuekd"]
                                                                               [:field/type :text]
The walker selector worked fine, but now :group/fields has gone from a vector with a single map to a vector, that has a single list of k/v pairs including the new one. So I’m obviously doing somethign wrong lol. When if leave off the AFTER-ELEM, the map is set as one would expect but replaces the one that was ‘found’ I’ve figured that I need to ‘go up/out’ of the map to get back to the vector, at this point, just not sure how

nathanmarz21:12:09

@eoliphant you need to do something similar to what I showed you before with before-index-dynamic, except navigate after the index of the target element

eoliphant21:12:01

hmm, ok so would it be something more like creating a recursive-path navigator instead of using walker?

nathanmarz21:12:58

make a recursive path navigating to every "group" map, then navigate from there to the position next to your target element

nathanmarz21:12:57

there are really very few cases that justify the use of walker

nathanmarz21:12:06

it traverses map keys, map key/value pairs, dives into record fields, all of which causes it to be much slower than necessary as well as lead to surprising bugs

eoliphant21:12:33

ok cool, that’s ironically sort of what I was thinking 🙂

eoliphant21:12:42

and i see your point, walker is quick-and-dirty but far from optimal since I know specifically what I need to ’walk;

eoliphant23:12:44

hi, I’m trying something like this:

(def find-field
  (sp/recursive-path [uuid] p
                     (sp/cond-path
                       (sp/keypath :group/subgroups) [sp/ALL p]
                       (sp/keypath :group/fields) [sp/ALL p]
                       #(= (:field/id %) uuid) sp/STAY)))
and calling it like so
(sp/select [:designer/cur-form-def
            :form/groups
            (find-field #uuid "b9a2c9ea-2647-48d3-ab26-4c2a115d7b6f" )
            ]
            appdb)
I’ve tried it with and without the keypath call but I’m getting a “Don’t know how to create ISeq from: clojure.lang.Keyword ” in both cases