I think I've got a use case that specter might help, but I'm brand new to it. I've got data that looks like this:
{:name "Conditional Sections E2E Workflow"
:application {:name "Conditional Sections"
:id "someId"}}
There's nested maps, and any time there's an :id
key in the map, I need to look up the value of :id
in another map, and assoc that value in place.I'm reading about navigators now... if y'all have a pointer to the one/ones I need, I'd appreciate it
usually the best way to get help around here is to give a sample input and output. Someone will invariably take up the challenge as a little brain teaser to occupy themselves while they wait for their next meeting to start and post the answer
I can do that, I'll find a good one
Okay, suppose there is a map called state
floating about, like shown. Here's the given data, and the desired data:
{:state {"FzPIxXKk" {:name "Conditional Sections E2E Workflow"
:id "var"}}
:given {:name "Conditional Sections E2E Workflow"
:application {:name "Conditional Sections"
:id "FzPIxXKk"}}
:desired {:name "Conditional Sections E2E Workflow"
:application {:name "Conditional Sections"
:id "var"}}}
For reference, here is the brittle "let's hope we got all the cases" code I have for doing this:
(defn return-with-id [exchange state]
(assoc-in exchange [:required :id] (get (get state (get-in exchange [:required :id])) :id)))
(defn return-with-application [exchange state]
(assoc-in exchange [:required :application :id] (get (get state (get-in exchange [:required :application :id])) :id)))
(defn return-with-parent [exchange state]
(assoc-in exchange [:required :parent :id] (get (get state (get-in exchange [:required :parent :id])) :id)))
(defn return-with-target [exchange state]
(assoc-in exchange [:required :target :id] (get (get state (get-in exchange [:required :target :id])) :id)))
(defn return [state exchange]
(if (seq? exchange)
(throw (Exception. "Multiple requirements needed, invalidating my assumption about our data"))
(cond-> exchange
(get state (get-in exchange [:required :id])) (return-with-id state)
(get state (get-in exchange [:required :application :id])) (return-with-application state)
(get state (get-in exchange [:required :target :id])) (return-with-target state)
(get state (get-in exchange [:required :parent :id])) (return-with-parent state))))
I could replace those copypasta functions with one function that takes a path -- but it would still be, explicit paths.
Then I'd have,
(defn update-with-path [state exchange path]
(cond-> exchange
(get state (get-in exchange (concat [:required] path)))
(assoc-in (concat [:required] path) (get (get state (get-in exchange (concat [:required] path))) :id))))
(defn return [state exchange]
(if (seq? exchange)
(throw (Exception. "Multiple requirements needed, invalidating my assumption about our data"))
(let [update-fn (partial update-with-path state)]
(-> exchange
(update-fn [:id])
(update-fn [:application :id])
(update-fn [:target :id])
(update-fn [:parent :id])))))
And hey, that does work:
{:state {"FzPIxXKk" {:name "Conditional Sections E2E Workflow"
:id "var"}}
:given {:method "POST"
:url "<http://localhost:8080/api/v1/internal/workflows>"
:required {:name "Conditional Sections E2E Workflow"
:application {:name "Conditional Sections"
:id "FzPIxXKk"}}} ;; <-- needs updating
:result {:method "POST"
:url "<http://localhost:8080/api/v1/internal/workflows>"
:required {:name "Conditional Sections E2E Workflow"
:application {:name "Conditional Sections"
:id "var"}}}} ;; <-- updated
but like i said, not generic, seems brittle, gotta know what paths to :id's exist in order to explicitly supply them
@mathpunk take a look at recursive-path
, MAP-VALS
, and stay-then-continue
you can define a navigator that goes to every nested map, and then from there do your lookup of the id
e.g. (transform [ALL-MAPS (must :id)] (fn [id] (lookup-val-for-id id)) data)
great, thank you!