diff --git a/CHANGES.md b/CHANGES.md index b94fa7e..464b8de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ * Add `vtransform` variant of `transform` that takes in collected values as a vector in the first argument rather than spliced into argument list. * Add `vterminal` that takes in collected vals as vector in first argument rather than spliced into argument list. +* Add `compact` navigator. After each step of navigation of its subpath, `compact` removes the collection if it's empty. * Change `terminal` to be a no-op on select codepath ## 1.0.5 diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index 309e2ff..3ee7847 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -1455,3 +1455,11 @@ (cond-path (pred afn) STAY coll? [ALL-WITH-META p] ))) + +(let [compact* (stay-then-continue (if-path empty? (terminal-val NONE)))] + (defdynamicnav compact + "During transforms, after each step of navigation in subpath check if the + value is empty. If so, remove that value by setting it to NONE." + [& path] + (interleave (repeat (count path) compact*) path) + )) diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index f9e6480..e5e0cd1 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1662,6 +1662,21 @@ (is (= {:a 6} (vtransform [:a (s/putval 2) (s/putval 3)] (fn [vs v] (+ v (reduce + vs))) {:a 1}))) ) +(deftest compact-test + (is (= {} (setval [:a (s/compact :b :c)] s/NONE {:a {:b {:c 1}}}))) + (is (= {:a {:d 2}} (setval [:a (s/compact :b :c)] s/NONE {:a {:b {:c 1} :d 2}}))) + (let [TREE-VALUES (recursive-path [] p (s/if-path vector? [(s/compact s/ALL) p] s/STAY)) + tree [1 [2 3] [4 [[5] [[6]]]]]] + (is (= [2 4 6] (select [TREE-VALUES even?] tree))) + (is (= [1 [3] [[[5]]]] (setval [TREE-VALUES even?] s/NONE tree))) + ) + (is (= [{:a [{:c 1}]}] + (setval [s/ALL (s/compact :a s/ALL :b)] + s/NONE + [{:a [{:b 3}]} + {:a [{:b 2 :c 1}]}]))) + ) + #?(:clj (do (defprotocolpath FooPP)