diff --git a/CHANGES.md b/CHANGES.md index dde42bd..9dd87af 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,12 +1,13 @@ ## 0.13.3-SNAPSHOT * Transform to `com.rpl.specter/NONE` to remove elements from data structures. Works with `keypath` (for both sequences and maps), `must`, `ALL`, and `MAP-VALS` -* Dynamic navs automatically compile sequence returns if completely static -* Eliminate reflection warnings for clj (thanks @mpenet) +* Add `nthpath` navigator * Add `with-fresh-collected` higher order navigator * Added `traverse-all` which returns a transducer that traverses over all elements matching the given path. * `select-first` and `select-any` now avoid traversal beyond the first value matched by the path (like when using `ALL`), so they are faster now for those use cases. * Improved `ALL` performance for PersistentHashSet +* Dynamic navs automatically compile sequence returns if completely static +* Eliminate reflection warnings for clj (thanks @mpenet) ## 0.13.2 diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index b37849a..4a14685 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -796,6 +796,7 @@ (def keypath (eachnav n/keypath*)) (def must (eachnav n/must*)) +(def nthpath (eachnav n/nthpath*)) (defrichnav diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index c9493c5..490ca86 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -481,7 +481,8 @@ (assoc structure key newv)))) (defrichnav - ^{:doc "Navigates to the specified key, navigating to nil if it does not exist."} + ^{:doc "Navigates to the specified key, navigating to nil if it does not exist. + Setting the value to NONE will remove it from the collection."} keypath* [key] (select* [this vals structure next-fn] @@ -492,7 +493,8 @@ (defrichnav - ^{:doc "Navigates to the key only if it exists in the map."} + ^{:doc "Navigates to the key only if it exists in the map. Setting the value to NONE + will remove it from the collection."} must* [k] (select* [this vals structure next-fn] @@ -503,3 +505,26 @@ (if (contains? structure k) (do-keypath-transform vals structure k next-fn) structure))) + +(defnav nthpath* + ^{:doc "Navigates to the given position in the sequence. Setting the value to NONE + will remove it from the sequence. Works for all sequence types."} + [i] + (select* [this structure next-fn] + (next-fn (nth structure i))) + (transform* [this structure next-fn] + (if (vector? structure) + (let [newv (next-fn (nth structure i))] + (if (identical? newv i/NONE) + (i/srange-transform* structure i (inc i) (fn [_] [])) + (assoc structure i newv))) + (i/srange-transform* ; can make this much more efficient with alternate impl + structure + i + (inc i) + (fn [[e]] + (let [v (next-fn e)] + (if (identical? v i/NONE) + [] + [v]) + )))))) diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index facf982..4dbc2ad 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1322,6 +1322,14 @@ (and (pred ret) (= ret v))) +(deftest nthpath-test + (is (predand= vector? [1 2 -3 4] (transform (s/nthpath 2) - [1 2 3 4]))) + (is (predand= vector? [1 2 4] (setval (s/nthpath 2) s/NONE [1 2 3 4]))) + (is (predand= (complement vector?) '(1 -2 3 4) (transform (s/nthpath 1) - '(1 2 3 4)))) + (is (predand= (complement vector?) '(1 2 4) (setval (s/nthpath 2) s/NONE '(1 2 3 4)))) + (is (= [0 1 [2 4 4]] (transform (s/nthpath 2 1) inc [0 1 [2 3 4]]))) + ) + (deftest remove-with-NONE-test (is (predand= vector? [1 2 3] (setval [s/ALL nil?] s/NONE [1 2 nil 3 nil]))) (is (predand= list? '(1 2 3) (setval [s/ALL nil?] s/NONE '(1 2 nil 3 nil))))