From b13b8d3c35dc623e0dc127de1b333af2b17d4de7 Mon Sep 17 00:00:00 2001 From: nathanmarz Date: Wed, 1 Mar 2017 10:40:17 -0500 Subject: [PATCH] add remove with NONE functionality to FIRST and LAST --- CHANGES.md | 2 +- src/clj/com/rpl/specter/navs.cljc | 55 ++++++++++++++++++++++------- test/com/rpl/specter/core_test.cljc | 38 ++++++++++++++++++++ 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9f541a5..ebfaed8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ ## 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` +* Transform to `com.rpl.specter/NONE` to remove elements from data structures. Works with `keypath` (for both sequences and maps), `must`, `nthpath`, `ALL`, `MAP-VALS`, `FIRST`, and `LAST` * 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. diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index 3848391..636749d 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -476,10 +476,18 @@ (updater structure next-fn)))) (defn- update-first-list [l afn] - (cons (afn (first l)) (rest l))) + (let [newf (afn (first l)) + restl (rest l)] + (if (identical? i/NONE newf) + restl + (cons newf restl)))) (defn- update-last-list [l afn] - (concat (butlast l) [(afn (last l))])) + (let [lastl (afn (last l)) + bl (butlast l)] + (if (identical? i/NONE lastl) + (if (nil? bl) '() bl) + (concat bl [lastl])))) #?( :clj @@ -504,26 +512,49 @@ (extend-protocol UpdateExtremes #?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector) (update-first [v afn] - (let [val (nth v 0)] - (assoc v 0 (afn val)))) + (let [val (nth v 0) + newv (afn val)] + (if (identical? i/NONE newv) + (subvec v 1) + (assoc v 0 newv) + ))) (update-last [v afn] ;; type-hinting vec-count to ^int caused weird errors with case (let [c (int (vec-count v))] (case c - 1 (let [[e] v] [(afn e)]) - 2 (let [[e1 e2] v] [e1 (afn e2)]) - (let [i (dec c)] - (assoc v i (afn (nth v i))))))) + 1 (let [[e] v + newe (afn e)] + (if (identical? i/NONE newe) + [] + [newe])) + 2 (let [[e1 e2] v + newe (afn e2)] + (if (identical? i/NONE newe) + [e1] + [e1 newe])) + (let [i (dec c) + newe (afn (nth v i))] + (if (identical? i/NONE newe) + (pop v) + (assoc v i newe)))))) #?(:clj String :cljs string) (update-first [s afn] - (str (afn (nth s 0)) (subs s 1 (count s)))) + (let [rests (subs s 1 (count s)) + newb (afn (nth s 0))] + (if (identical? i/NONE newb) + rests + (str newb rests)))) (update-last [s afn] - (let [last-idx (-> s count dec)] - (str (subs s 0 last-idx) (afn (nth s last-idx))) - )) + (let [last-idx (-> s count dec) + newl (afn (nth s last-idx)) + begins (subs s 0 last-idx)] + (if (identical? i/NONE newl) + begins + (str begins newl) + ))) #?(:clj Object :cljs default) (update-first [l val] diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index 5cfc022..790f409 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1469,3 +1469,41 @@ (= (select s/MAP-KEYS m) (select [s/ALL s/FIRST] m) ))) + +(defspec remove-first-vector + (for-all+ + [v (limit-size 10 (gen/not-empty (gen/vector gen/int)))] + (let [newv (setval s/FIRST s/NONE v)] + (and (= newv (vec (rest v))) + (vector? newv) + )))) + +(defspec remove-first-list + (for-all+ + [l (limit-size 10 (gen/not-empty (gen/list gen/int)))] + (let [newl (setval s/FIRST s/NONE l)] + (and (= newl (rest l)) + (list? newl) + )))) + +(defspec remove-last-vector + (for-all+ + [v (limit-size 10 (gen/not-empty (gen/vector gen/int)))] + (let [newv (setval s/LAST s/NONE v)] + (and (= newv (vec (butlast v))) + (vector? newv) + )))) + +(defspec remove-last-list + (for-all+ + [l (limit-size 10 (gen/not-empty (gen/list gen/int)))] + (let [newl (setval s/LAST s/NONE l) + bl (butlast l)] + (and (or (= newl bl) (and (nil? bl) (= '() newl))) + (seq? newl) + )))) + +(deftest remove-extreme-string + (is (= "b" (setval s/FIRST s/NONE "ab"))) + (is (= "a" (setval s/LAST s/NONE "ab"))) + )