I'm wondering if there is a more idiomatic way to accomplish the following transformation:
I have a vector of maps that each contain a vector of children:
(def a-structure [{:children [{:a 1 :b 2} {:a 3 :b 4}] :something "a"}
{:children [{:a 4 :b 5} {:a 6 :b 10}] :something "b"}])
I want to transform the parent maps, and also filter and transform each of the child maps.
I came up with the following, where I do the filter and transform of children in two separate paths:
(sp/multi-transform
[sp/ALL (sp/multi-path
[(sp/terminal (fn [thing-with-children]
{:new-thing 1
:something-renamed (:something thing-with-children)
:children (:children thing-with-children)}))] ; apply transform to top-level object
[:children (sp/filterer [(sp/must :a) (sp/pred<= 1)]) (sp/terminal sp/NONE)] ; remove duds
[:children
sp/ALL
(sp/terminal (fn [child] {:a-renamed (:a child) :b-renamed (:b child)}))])] ; apply transform to each non-filtered child
a-structure)
Ignoring the fact that I'm just renaming keys, how can I make this transformation better?
I feel like the second path replacing filtered children with NONE can't be the right way, but I can't figure out how to merge it with the last path, that actually transforms the unfiltered children.I tried inlining the filter:
(sp/multi-transform
[sp/ALL (sp/multi-path
[(sp/terminal (fn [thing-with-children]
{:new-thing 1
:something-renamed (:something thing-with-children)
:children (:children thing-with-children)}))] ; apply transform to top-level object
[:children
(sp/filterer [(sp/must :a) (sp/pred> 1)]) ; remove duds
sp/ALL
(sp/terminal (fn [child] {:a-renamed (:a child) :b-renamed (:b child)}))])] ; apply transform to each non-filtered child
a-structure)
But the :children vector still contains the "filtered" item, because the first path includes :children in the top-level result
[{:new-thing 1,
:something-renamed "a",
:children [{:a 1, :b 2} {:a-renamed 3, :b-renamed 4}]}
{:new-thing 1,
:something-renamed "c",
:children [{:a-renamed 4, :b-renamed 5} {:a-renamed 6, :b-renamed 10}]}]
(notice the first child in the first result is not-transformed)map-key is the navigator to use for renaming keys
Thanks! What do you think about
[:children (sp/filterer [(sp/must :a) (sp/pred<= 1)]) (sp/terminal sp/NONE)]
It seems like "select things to filter, replace them with NONE so they are omitted", which feels redundantyou need terminal-val there
[:children ALL (sp/selected? (sp/must :a) (sp/pred<= 1)) (sp/terminal-val sp/NONE)] is more performant