diff --git a/src/clj/com/rpl/specter.clj b/src/clj/com/rpl/specter.clj index 399adfd..75725ac 100644 --- a/src/clj/com/rpl/specter.clj +++ b/src/clj/com/rpl/specter.clj @@ -107,6 +107,9 @@ (defmacro viewfn [& args] `(view (fn ~@args))) +(defn split [& selectors] + (->SplitPath (->> selectors (map comp-paths*) doall))) + (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 diff --git a/src/clj/com/rpl/specter/impl.clj b/src/clj/com/rpl/specter/impl.clj index 4e66019..8051bc7 100644 --- a/src/clj/com/rpl/specter/impl.clj +++ b/src/clj/com/rpl/specter/impl.clj @@ -117,8 +117,8 @@ (defn- walk-until [pred on-match-fn structure] (if (pred structure) - (on-match-fn structure) - (walk/walk (partial walk-until pred on-match-fn) identity structure) + (on-match-fn structure) + (walk/walk (partial walk-until pred on-match-fn) identity structure) )) (defn- fn-invocation? [f] @@ -130,7 +130,7 @@ (if (pred structure) (on-match-fn structure) (let [ret (walk/walk (partial codewalk-until pred on-match-fn) identity structure)] - (if (and (fn-invocation? structure) (fn-invocation? ret)) + (if (and (fn-invocation? structure) (fn-invocation? ret)) (with-meta ret (meta structure)) ret )))) @@ -159,7 +159,7 @@ [(conj s e) (assoc m pos i)] orig ))) - [[] {}] + [[] {}] (range (count aseq)) ))) @@ -270,5 +270,10 @@ (-> structure view-fn next-fn) )) - - +(deftype SplitPath [selectors] + StructureValsPath + (select-full* [this vals structure next-fn] + (into [] (r/mapcat #(select-full* % vals structure next-fn) selectors))) + (update-full* [this vals structure next-fn] + (reduce (fn [structure s] (update-full* s vals structure next-fn)) structure selectors) + )) diff --git a/test/clj/com/rpl/specter/core_test.clj b/test/clj/com/rpl/specter/core_test.clj index b892053..0290b61 100644 --- a/test/clj/com/rpl/specter/core_test.clj +++ b/test/clj/com/rpl/specter/core_test.clj @@ -3,7 +3,7 @@ [clojure.test.check.clojure-test] [com.rpl specter] [com.rpl.specter test-helpers]) - (:require [clojure.test.check + (:require [clojure.test.check [generators :as gen] [properties :as prop]] [clojure.test.check :as qc])) @@ -24,7 +24,7 @@ (defspec select-all-keyword-filter (for-all+ [kw gen/keyword - v (gen/vector (max-size 5 + v (gen/vector (max-size 5 (gen-map-with-keys gen/keyword gen/int kw))) pred (gen/elements [odd? even?])] (= (select [ALL kw pred] v) @@ -157,7 +157,7 @@ ))) (defspec replace-in-test - (for-all+ + (for-all+ [v (gen/vector gen/int)] (let [res (->> v (map (fn [v] (if (even? v) (inc v) v)))) user-ret (->> v @@ -170,7 +170,7 @@ )))) (defspec replace-in-custom-merge - (for-all+ + (for-all+ [v (gen/vector gen/int)] (let [res (->> v (map (fn [v] (if (even? v) (inc v) v)))) last-even (->> v (filter even?) last) @@ -227,3 +227,15 @@ [:a] [[1 3 5] [2] [7 11 4 2] [10 1] []] )))) + +(deftest split-test + (let [data {:a [1 2 3 4] :b {:c :d}}] + (is (= (select (split [:a ALL even?] [:b :c]) data) + [2 4 :d])) + ;;TODO: this behavior is highly confusing... any way to have split selectors go first and THEN updates applied? ... only affects updates... what if it matches both selectors? + ;; maybe shouldn't allow splitting for ALL + (is (= (update [:a ALL (split [even? (view -)] odd?)] + inc + data) + {:a [2 0 4 -2] :b {:c :d}} + ))))