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

(require '[vvvvalvalval.supdate.api :as supd])

(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 "robert 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

(supd/supdate [2 3 5 7 11 13 17 19] [inc])

(supd/supdate (range 12) [#(* % %)])

Transforming maps

(supd/supdate {:name "Alice" :age 7} {:age inc})

(supd/supdate {:name "Ilúvatar"} {:age inc})

false

(supd/supdate {:name "Alice" :age 7} {:age false})

Chaining tranforms

(supd/supdate " jimmy page " [str/trim str/capitalize])

(supd/supdate (range 10) [[inc inc inc]])

Composing tranforms

supdate

(def input1 {:band/members [{:id 1 :name ["jimmy" "page"] :plays "guitar"} {:id 2 :name ["robert" "plant"] :plays "voice"} {: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 "robert 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

Github issue

Ths page is live and interactive powered by the klipse plugin: