Popping in to say my project adopted Specter a few days ago after much hemming and hawing about doing so, and my god our code is so much tidier already. A good chunk of our namespaces are going to be able to cut their size in half or more. Thanks @nathanmarz for the great work! "Clojure's missing piece" is a bold claim, but Specter lives up to it.
There is one transformation I keep finding I have to do, which I haven't found a way to do with Specter yet: moving values from one level of a map hierarchy to another, under a different name, in bulk. Like set/rename-keys, but deep. E.g., start with this structure
[{:a 1, :b 2}
{:a 3, :b 4}]
and end up with this one
[{:x {:y 1, :z {:w 2}}}
{:x {:y 3, :z {:w 4}}}]
Does Specter have a way to do this sort of thing that I haven't found yet? EDIT: Tweaked output to illustrate that values may not all land at the same level.well, that's a complete transformation of almost the entire thing
Specter can assist, but the behavior all needs to be specified
I'm not sure what the rules are supposed to be in this case
just dispatch on the name of the key?
I guess what I'm reaching for is a way to transform one keypath to another keypath. And then, ideally, do that for multiple keypaths that share a prefix without repeating that prefix over and over.
this does what you want:
(multi-transform
[ALL
(multi-path
[ALL
(terminal
(fn [[k v]]
(cond (= k :a)
[:y v]
(= k :b)
[:z {:w v}]
:else
[k v]
)))]
(terminal #(hash-map :x %)))]
data)doesn't seem like you want to specify it like that though
you want something like (foo {[:a] [:x :y] [:b] [:x :z :w]} data)?
Less contrived, similar to my actual problem shape:
{:edges [{:node {:fooDetail {:fooName "A", :fooDesc "B", :fooBarName "C"}}}
{:node {:fooDetail {:fooName "D", :fooDesc "E", :fooBarName "F"}}}]}
into
{:edges [{:node {:foo {:name "A", :desc "B", :bar {:name "C"}}}}
{:node {:foo {:name "D", :desc "E", :bar {:name "F"}}}}]}
Yes, that's basically what is dimly taking shape in my head.
Could straightforwardly write a function to do that for maps-all-the-way-down structures, but I have some cases where integrating with Specter navigators would be handy.
Specter can help, but major restructurings of data like this aren't going to be one of those examples that are solved by just stringing a couple navigators together
Fair enough! Thanks for the guidance.
It did just occur to me I could write a (foo {[:a] [:x :y] [:b] [:x :z :w]} data) which makes calls to Specter in its implementation, instead of trying to get clever with advanced navigators.
yup
(sp/transform [:edges sp/ALL :node :fooDetail]
(fn [node]
(remap node {[:fooName] [:name]
[:fooDesc] [:desc]
[:fooBarName] [:bar :name]}))
input)
{:edges
[{:node {:fooDetail {:name "A", :desc "B", :bar {:name "C"}}}}
{:node {:fooDetail {:name "D", :desc "E", :bar {:name "F"}}}}]}
Works like a charm. Thanks again!(of course that example could implement remap without Specter too, but in principle this is more powerful, and probably also faster)