Supdate (Clojure library for transforming nested data structures)
source link: https://www.tuicool.com/articles/3URbU3Z
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Supdate
supdate
is a small Clojure/ClojureScript library
for transforming nested data structures in a straightforward and efficient way.
supdate
lets you express nested transformations as data structures which match the schema of the input,
and then executes these transformations efficiently.
You could say supdate
is to data transformation what plumatic/schema
is to data validation. It plays in the same arena as Specter
, complementing
it nicely for many basic cases where Specter would be too much.
Example
The library can be downloaded from Clojars. Require it with the following:
(require '[vvvvalvalval.supdate.api :as supd])
Here's a basic example (it's interactive, feel free to modify the code!):
(require '[clojure.string :as str])
;; Our example uses some string functions (require '[clojure.string :as str]) ;; Let's define some example data (def input {:bands [{:band/id 3141 :band/name "Led Zeppelin" :band/members [{:person/name "roger plant"} {:person/name "jimmy page"} {:person/name "john bonham"} {:person/name "john paul jones"}]} {:band/id 8242 :band/name "The White stripes" :band/members [{:person/name "jack white"} {:person/name "meg white"}]}]}) ;; ... then transform it: (supd/supdate input {:bands [{:band/name str/upper-case :band/members [{:person/name str/capitalize}]}]})
The rules
supdate
works by defining transforms
as data structures.
The most elementary transform you can encounter is just a function:
(supd/supdate 0 inc)
(supd/supdate "Jimmy Page" (fn [s] (str/split s " ")))
Transforming sequences
Wrapping a transform in a vector means you want to apply it to a sequence of inputs:
(supd/supdate [2 3 5 7 11 13 17 19] [inc])
(supd/supdate (range 12) [#(* % %)])
Transforming maps
Wrapping a transform in a map means you want to transform the value at the key:
(supd/supdate {:name "Alice" :age 7} {:age inc})
Note that the transformation only occurs if the key is present!
(supd/supdate {:name "Ilúvatar"} {:age inc})Finally, if you use
false
as a value instead of a transform, it means you want the key to be dissoc'ed:
(supd/supdate {:name "Alice" :age 7} {:age false})
Chaining tranforms
Recall that if you put a transform in a one-element vector, it is applied sequentially. If you put severaltransforms in a vector, it does something else: it applies the transforms one after the other, left to right.
(supd/supdate " jimmy page " [str/trim str/capitalize])
What if you want to do both, chaining tranforms and applying them in sequences? Well, you can use 2 vectors:
(supd/supdate (range 10) [[inc inc inc]])
Composing tranforms
supdate
really becomes interesting when you start nesting transforms together:
(def input1 {:band/members [{:id 1 :name ["jimmy" "page"] :plays "voice"} {:id 2 :name ["robert" "plant"] :plays "guitar"} {:id 3 :name ["john" "paul" "jones"] :plays "bass"} {:id 4 :name ["john" "bonham"] :plays "drums"}]}) (supd/supdate input1 {:band/members [{:id false :name [[str/capitalize] #(str/join " " %)] :plays keyword}]})
Is supdate
fast?
Yes! Supdate will tend to outperform code hand-written using update-in
or assoc-in
, using several optimizations.
It will avoid doing several 'deep dives' in the data, doing all it has to do once at a given path.
What's more, supd/supdate
is actually a macro, leveraging static information on a best-effort basis to compile to efficient code.
Finally, you can also use supd/compile
to pre-compile transforms, and achieve a similar effect more dynamically:
(def input {:bands [{:band/id 3141 :band/name "Led Zeppelin" :band/members [{:person/name "roger plant"} {:person/name "jimmy page"} {:person/name "john bonham"} {:person/name "john paul jones"}]} {:band/id 8242 :band/name "The White stripes" :band/members [{:person/name "jack white"} {:person/name "meg white"}]}]}) ;; Pre-compiling our transform (def prettify-bands (supd/compile {:bands [{:band/name str/upper-case :band/members [{:person/name str/capitalize}]}]})) ;; Running it later: (prettify-bands input)
Parting words
Feel free to give feedback by opening aGithub issue
!
Ths page is live and interactive powered by the klipse plugin :
- Live : The code is executed in your browser
- Interactive : You can modify the code and it is evaluated as you type
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK