change val-select* to collect*, rename comp-structure-paths to comp-paths, clean up names of protocols, add selected? function, update README
This commit is contained in:
parent
ea05b98280
commit
45260ff9c1
5 changed files with 54 additions and 32 deletions
|
|
@ -85,7 +85,7 @@ user> (select (walker number?)
|
||||||
When doing more involved transformations, you often find you lose context when navigating deep within a data structure and need information "up" the data structure to perform the transformation. Specter solves this problem by allowing you to collect values during navigation to use in the update function. Here's an example which transforms a sequence of maps by adding the value of the :b key to the value of the :a key, but only if the :a key is even:
|
When doing more involved transformations, you often find you lose context when navigating deep within a data structure and need information "up" the data structure to perform the transformation. Specter solves this problem by allowing you to collect values during navigation to use in the update function. Here's an example which transforms a sequence of maps by adding the value of the :b key to the value of the :a key, but only if the :a key is even:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
user> (update [ALL (val-selector-one :b) :a even?]
|
user> (update [ALL (collect-one :b) :a even?]
|
||||||
+
|
+
|
||||||
[{:a 1 :b 3} {:a 2 :b -10} {:a 4 :b 10} {:a 3}])
|
[{:a 1 :b 3} {:a 2 :b -10} {:a 4 :b 10} {:a 3}])
|
||||||
[{:b 3, :a 1} {:b -10, :a -8} {:b 10, :a 14} {:a 3}]
|
[{:b 3, :a 1} {:b -10, :a -8} {:b 10, :a 14} {:a 3}]
|
||||||
|
|
@ -93,7 +93,7 @@ user> (update [ALL (val-selector-one :b) :a even?]
|
||||||
|
|
||||||
The update function receives as arguments all the collected values followed by the navigated to value. So in this case `+` receives the value of the :b key followed by the value of the :a key, and the update is performed to :a's value.
|
The update function receives as arguments all the collected values followed by the navigated to value. So in this case `+` receives the value of the :b key followed by the value of the :a key, and the update is performed to :a's value.
|
||||||
|
|
||||||
The three built-in ways for collecting values are `VAL`, `val-selector`, and `val-selector-one`. `VAL` just adds whatever element it's currently on to the value list, while `val-selector` and `val-selector-one` take in a selector to navigate to the desired value. `val-selector` works just like `select` by finding a sequence of values, while `val-selector-one` expects to only navigate to a single value.
|
The three built-in ways for collecting values are `VAL`, `collect`, and `collect-one`. `VAL` just adds whatever element it's currently on to the value list, while `collect` and `collect-one` take in a selector to navigate to the desired value. `collect` works just like `select` by finding a sequence of values, while `collect-one` expects to only navigate to a single value.
|
||||||
|
|
||||||
To make your own selector, implement the `StructurePath` protocol which looks like:
|
To make your own selector, implement the `StructurePath` protocol which looks like:
|
||||||
|
|
||||||
|
|
@ -106,10 +106,10 @@ To make your own selector, implement the `StructurePath` protocol which looks li
|
||||||
|
|
||||||
Looking at the implementations of the built-in selectors should provide you with the guidance you need to make your own selectors.
|
Looking at the implementations of the built-in selectors should provide you with the guidance you need to make your own selectors.
|
||||||
|
|
||||||
Finally, you can make `select` and `update` work much faster by precompiling your selectors using the `comp-structure-paths` function. There's about a 5x speed difference between the following two invocations of update:
|
Finally, you can make `select` and `update` work much faster by precompiling your selectors using the `comp-paths` function. There's about a 5x speed difference between the following two invocations of update:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(def precompiled (comp-structure-paths ALL :a even?))
|
(def precompiled (comp-paths ALL :a even?))
|
||||||
|
|
||||||
(update [ALL :a even?] structure)
|
(update [ALL :a even?] structure)
|
||||||
(update precompiled structure)
|
(update precompiled structure)
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
;;there are going to be. this should make it much easier to allocate space for vals without doing concats
|
;;there are going to be. this should make it much easier to allocate space for vals without doing concats
|
||||||
;;all over the place. The apply to the vals + structure can also be avoided since the number of vals is known
|
;;all over the place. The apply to the vals + structure can also be avoided since the number of vals is known
|
||||||
;;beforehand
|
;;beforehand
|
||||||
(defn comp-structure-paths [& structure-paths]
|
(defn comp-paths [& paths]
|
||||||
(comp-structure-paths* (vec structure-paths)))
|
(comp-paths* (vec paths)))
|
||||||
|
|
||||||
;; Selector functions
|
;; Selector functions
|
||||||
|
|
||||||
(defn select [selector structure]
|
(defn select [selector structure]
|
||||||
(let [sp (comp-structure-paths* selector)]
|
(let [sp (comp-paths* selector)]
|
||||||
(select-full* sp
|
(select-full* sp
|
||||||
[]
|
[]
|
||||||
structure
|
structure
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
;; Update functions
|
;; Update functions
|
||||||
|
|
||||||
(defn update [selector update-fn structure]
|
(defn update [selector update-fn structure]
|
||||||
(let [selector (comp-structure-paths* selector)]
|
(let [selector (comp-paths* selector)]
|
||||||
(update-full* selector
|
(update-full* selector
|
||||||
[]
|
[]
|
||||||
structure
|
structure
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
|
|
||||||
(def ALL (->AllStructurePath))
|
(def ALL (->AllStructurePath))
|
||||||
|
|
||||||
(def VAL (->ValStructurePath))
|
(def VAL (->ValCollect))
|
||||||
|
|
||||||
(def LAST (->LastStructurePath))
|
(def LAST (->LastStructurePath))
|
||||||
|
|
||||||
|
|
@ -107,6 +107,18 @@
|
||||||
(defmacro viewfn [& args]
|
(defmacro viewfn [& args]
|
||||||
`(view (fn ~@args)))
|
`(view (fn ~@args)))
|
||||||
|
|
||||||
|
(defn selected?
|
||||||
|
"Filters the current value based on whether a selector finds anything.
|
||||||
|
e.g. (selected? :vals ALL even?) keeps the current element only if an
|
||||||
|
even number exists for the :vals key"
|
||||||
|
[& selectors]
|
||||||
|
(let [s (comp-paths selectors)]
|
||||||
|
(fn [structure]
|
||||||
|
(->> structure
|
||||||
|
(select s)
|
||||||
|
empty?
|
||||||
|
not))))
|
||||||
|
|
||||||
(extend-type clojure.lang.Keyword
|
(extend-type clojure.lang.Keyword
|
||||||
StructurePath
|
StructurePath
|
||||||
(select* [kw structure next-fn]
|
(select* [kw structure next-fn]
|
||||||
|
|
@ -125,8 +137,8 @@
|
||||||
(next-fn structure)
|
(next-fn structure)
|
||||||
structure)))
|
structure)))
|
||||||
|
|
||||||
(defn val-selector [& selector]
|
(defn collect [& selector]
|
||||||
(->SelectorValsPath select (comp-structure-paths* selector)))
|
(->SelectCollector select (comp-paths* selector)))
|
||||||
|
|
||||||
(defn val-selector-one [& selector]
|
(defn collect-one [& selector]
|
||||||
(->SelectorValsPath select-one (comp-structure-paths* selector)))
|
(->SelectCollector select-one (comp-paths* selector)))
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@
|
||||||
com.rpl.specter.protocols.StructureValsPath
|
com.rpl.specter.protocols.StructureValsPath
|
||||||
(coerce-path [this] this)
|
(coerce-path [this] this)
|
||||||
|
|
||||||
com.rpl.specter.protocols.ValPath
|
com.rpl.specter.protocols.Collector
|
||||||
(coerce-path [valpath]
|
(coerce-path [collector]
|
||||||
(reify StructureValsPath
|
(reify StructureValsPath
|
||||||
(select-full* [this vals structure next-fn]
|
(select-full* [this vals structure next-fn]
|
||||||
(next-fn (conj vals (select-val valpath structure)) structure))
|
(next-fn (conj vals (collect-val collector structure)) structure))
|
||||||
(update-full* [this vals structure next-fn]
|
(update-full* [this vals structure next-fn]
|
||||||
(next-fn (conj vals (select-val valpath structure)) structure))))
|
(next-fn (conj vals (collect-val collector structure)) structure))))
|
||||||
|
|
||||||
;; need to say Object instead of StructurePath so that things like Keyword are properly coerced
|
;; need to say Object instead of StructurePath so that things like Keyword are properly coerced
|
||||||
Object
|
Object
|
||||||
|
|
@ -37,12 +37,12 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
(extend-protocol StructurePathComposer
|
(extend-protocol StructureValsPathComposer
|
||||||
Object
|
Object
|
||||||
(comp-structure-paths* [sp]
|
(comp-paths* [sp]
|
||||||
(coerce-path sp))
|
(coerce-path sp))
|
||||||
java.util.List
|
java.util.List
|
||||||
(comp-structure-paths* [structure-paths]
|
(comp-paths* [structure-paths]
|
||||||
(reduce (fn [sp-curr sp]
|
(reduce (fn [sp-curr sp]
|
||||||
(reify StructureValsPath
|
(reify StructureValsPath
|
||||||
(select-full* [this vals structure next-fn]
|
(select-full* [this vals structure next-fn]
|
||||||
|
|
@ -182,9 +182,9 @@
|
||||||
(->> structure (r/map next-fn) (into empty-structure))
|
(->> structure (r/map next-fn) (into empty-structure))
|
||||||
))))
|
))))
|
||||||
|
|
||||||
(deftype ValStructurePath []
|
(deftype ValCollect []
|
||||||
ValPath
|
Collector
|
||||||
(select-val [this structure]
|
(collect-val [this structure]
|
||||||
structure))
|
structure))
|
||||||
|
|
||||||
(deftype LastStructurePath []
|
(deftype LastStructurePath []
|
||||||
|
|
@ -237,9 +237,9 @@
|
||||||
(key-update akey structure next-fn)
|
(key-update akey structure next-fn)
|
||||||
))
|
))
|
||||||
|
|
||||||
(deftype SelectorValsPath [sel-fn selector]
|
(deftype SelectCollector [sel-fn selector]
|
||||||
ValPath
|
Collector
|
||||||
(select-val [this structure]
|
(collect-val [this structure]
|
||||||
(sel-fn selector structure)))
|
(sel-fn selector structure)))
|
||||||
|
|
||||||
(deftype SRangePath [start-fn end-fn]
|
(deftype SRangePath [start-fn end-fn]
|
||||||
|
|
@ -269,3 +269,6 @@
|
||||||
(update* [this structure next-fn]
|
(update* [this structure next-fn]
|
||||||
(-> structure view-fn next-fn)
|
(-> structure view-fn next-fn)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
(select* [this structure next-fn])
|
(select* [this structure next-fn])
|
||||||
(update* [this structure next-fn]))
|
(update* [this structure next-fn]))
|
||||||
|
|
||||||
(defprotocol ValPath
|
(defprotocol Collector
|
||||||
(select-val [this structure]))
|
(collect-val [this structure]))
|
||||||
|
|
||||||
(defprotocol StructurePathComposer
|
(defprotocol StructureValsPathComposer
|
||||||
(comp-structure-paths* [structure-paths]))
|
(comp-paths* [paths]))
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
kw2 gen/keyword
|
kw2 gen/keyword
|
||||||
m (max-size 10 (gen-map-with-keys gen/keyword gen/int kw1 kw2))
|
m (max-size 10 (gen-map-with-keys gen/keyword gen/int kw1 kw2))
|
||||||
pred (gen/elements [odd? even?])]
|
pred (gen/elements [odd? even?])]
|
||||||
(= (update [(val-selector-one kw2) kw1 pred] + m)
|
(= (update [(collect-one kw2) kw1 pred] + m)
|
||||||
(if (pred (kw1 m))
|
(if (pred (kw1 m))
|
||||||
(assoc m kw1 (+ (kw1 m) (kw2 m)))
|
(assoc m kw1 (+ (kw1 m) (kw2 m)))
|
||||||
m
|
m
|
||||||
|
|
@ -208,7 +208,7 @@
|
||||||
|
|
||||||
(deftest structure-path-directly-test
|
(deftest structure-path-directly-test
|
||||||
(is (= 3 (select-one :b {:a 1 :b 3})))
|
(is (= 3 (select-one :b {:a 1 :b 3})))
|
||||||
(is (= 5 (select-one (comp-structure-paths :a :b) {:a {:b 5}})))
|
(is (= 5 (select-one (comp-paths :a :b) {:a {:b 5}})))
|
||||||
)
|
)
|
||||||
|
|
||||||
(defspec view-test
|
(defspec view-test
|
||||||
|
|
@ -220,3 +220,10 @@
|
||||||
(afn i)
|
(afn i)
|
||||||
(update (view afn) identity i)
|
(update (view afn) identity i)
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
(deftest selected?-test
|
||||||
|
(is (= [[1 3 5] [2 :a] [7 11 4 2 :a] [10 1 :a] []]
|
||||||
|
(setval [ALL (selected? ALL even?) END]
|
||||||
|
[:a]
|
||||||
|
[[1 3 5] [2] [7 11 4 2] [10 1] []]
|
||||||
|
))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue