I have a bit of data transformation that would benefit from specters multi-transform, but I hit some unexpected behaviour and would like to know if anyone has seen this before.
The data I am transforming looks a bit like this:
(def value {:albedo {:url "" :factor 1.0}
:normal {:url "foo/bar.png" :factor 0.1}
:tiling {:x 0.0 :y 0.0}
:offset {:x 0.0 :y 0.0}})
What I want to do is transform the keys :albedo
and :normal
, but only if the :url
key is not empty. I can do this using the following code:
(->> value
(specter/setval [(specter/multi-path :albedo :normal)
(specter/pred (comp empty? :url))]
specter/NONE)
(specter/transform [(specter/multi-path :albedo :normal)
(specter/must :url)]
(partial perform-computation)))
As I understand (https://github.com/nathanmarz/specter/wiki/List-of-Macros#multi-transform)[the Specter docs for multi-transform
] this could be written as:
(specter/multi-transform [(specter/multi-path :albedo :normal)
(specter/multi-path [(specter/pred (comp not-empty :url)) :url (specter/terminal (partial perform-computation))
(specter/pred (comp empty? :url)) (specter/terminal-val specter/NONE)])]
value)
However, this ends up only stripping entries with empty :url
. So, I decided to produce a simpler example using nested multi-path
s in a select
:
(specter/select [(specter/multi-path :a :b) (specter/multi-path :d :e)]
{:a {:d 23
:e 99}
:b {:d 11
:e 33}}) ;=> [23 99 11 33]
This shows that nesting multi-path
s should work as expected; it breaks when using multi-transform
though, where it only touches the first entry of the nested multi-path:
(specter/multi-transform [(specter/multi-path :a :b)
(specter/multi-path [:d (specter/terminal-val :UPDATED)
:e (specter/terminal-val :EEE)])]
{:a {:d 23
:e 99}
:b {:d 11
:e 33}}) ;=> {:a {:d :UPDATED :e 99} :b {:d :UPDATED :e 33}}
Has anyone seen this behaviour before, or can you spot what I am doing wrong? Any help would be much appreciated.