This commit is contained in:
Joshua Suskalo 2024-11-06 16:11:44 +00:00 committed by GitHub
commit 4af3627b90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 97 additions and 2 deletions

View file

@ -1,5 +1,6 @@
## 1.1.4
* Add SORTED, sorted, and sorted-by navs (thanks @IGJoshua)
* Add arglist metadata to navs (thanks @phronmophobic)
* Improve before-index performance by 150x on lists and 5x on vectors (thanks @jeff303)
* Bug fix: BEFORE-ELEM, AFTER-ELEM, FIRST, LAST, BEGINNING, and END on subvecs now produce vector type in cljs

View file

@ -1504,3 +1504,61 @@
[& path]
(map compact* path)
))
(defnav
^{:doc "Navigates to a sequence resulting from (sort ...), but is a
view to the original structure that can be transformed.
If the transformed sequence is smaller than the input sequence, values
which are included are sorted by the same indices as the input value's
index in the input sequence.
If the transformed sequence is larger than the input sequence, values
added to the end of the sequence will be appended to the end of the
original sequence."}
SORTED
[]
(select* [this structure next-fn]
(n/sorted-select structure identity compare next-fn))
(transform* [this structure next-fn]
(n/sorted-transform structure identity compare next-fn)))
(defnav
^{:doc "Navigates to a sequence resulting from (sort comparator ...), but
is a view to the original structure that can be transformed.
If the transformed sequence is smaller than the input sequence, values
which are included are sorted by the same indices as the input value's
index in the input sequence.
If the transformed sequence is larger than the input sequence, values
added to the end of the sequence will be appended to the end of the
original sequence."}
sorted
[comparator]
(select* [this structure next-fn]
(n/sorted-select structure identity comparator next-fn))
(transform* [this structure next-fn]
(n/sorted-transform structure identity comparator next-fn)))
(defdynamicnav sorted-by
"Navigates to a sequence sorted by the value stored in the keypath, by the
comparator, if one is provided.
This sequence is a view to the original structure that can be transformed. If
the transformed sequence is smaller than the input sequence, values which are
included are sorted by the same indices as the input value's index in the
input sequence.
If the transformed sequence is larger than the input sequence, values added to
the end of the sequence will be appended to the end of the original sequence.
Value collection (e.g. collect, collect-one) may not be used in the keypath."
([keypath] (sorted-by keypath compare))
([keypath comparator]
(late-bound-nav [late (late-path keypath)
late-fn comparator]
(select* [this structure next-fn]
(n/sorted-select structure #(compiled-select-one! late %) late-fn next-fn))
(transform* [this structure next-fn]
(n/sorted-transform structure #(compiled-select-one! late %) late-fn next-fn)))))

View file

@ -570,6 +570,18 @@
res
))))
(defn sorted-transform*
[structure keyfn comparator next-fn]
(let [sorted (sort-by (comp keyfn second) comparator (map-indexed vector structure))
indices (map first sorted)
result (next-fn (map second sorted))
unsorted (sort-by first compare (map vector indices result))]
(if (seq? structure)
(doall (map second unsorted))
(into (empty structure)
(map second)
unsorted))))
(defn- matching-indices [aseq p]
(keep-indexed (fn [i e] (if (p e) i)) aseq))

View file

@ -401,6 +401,11 @@
(def srange-transform i/srange-transform*)
(defn sorted-select
[structure keyfn comparator next-fn]
(next-fn (sort-by keyfn comparator structure)))
(def sorted-transform i/sorted-transform*)
(defn extract-basic-filter-fn [path]
(cond (fn? path)

View file

@ -1,6 +1,6 @@
(ns com.rpl.specter.core-test
#?(:cljs (:require-macros
[cljs.test :refer [is deftest]]
[cljs.test :refer [is deftest testing]]
[clojure.test.check.clojure-test :refer [defspec]]
[com.rpl.specter.cljs-test-helpers :refer [for-all+]]
[com.rpl.specter.test-helpers :refer [ic-test]]
@ -13,7 +13,7 @@
defdynamicnav traverse-all satisfies-protpath? end-fn
vtransform]]))
(:use
#?(:clj [clojure.test :only [deftest is]])
#?(:clj [clojure.test :only [deftest is testing]])
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
#?(:clj [com.rpl.specter.test-helpers :only [for-all+ ic-test]])
#?(:clj [com.rpl.specter
@ -1711,3 +1711,22 @@
(is (satisfies-protpath? FooPP "a"))
(is (not (satisfies-protpath? FooPP 1)))
)))
(deftest sorted-test
(let [initial-list [3 4 2 1]]
(testing "the SORTED navigator"
(is (= (sort initial-list) (select-one s/SORTED initial-list)))
(is (= [2 1 3 4] (transform s/SORTED reverse initial-list)))
(is (= [3 2 1] (transform s/SORTED butlast initial-list)))
(is (= [3 5 2 1] (setval [s/SORTED s/LAST] 5 initial-list)))
(is (= (list 1 2 3 4 5) (transform [s/SORTED s/ALL] inc (range 5)))))
(testing "the sorted navigator with comparator"
(let [reverse-comparator (comp - compare)]
(is (= (sort reverse-comparator initial-list)
(select-one (s/sorted reverse-comparator) initial-list)))
(is (= 4 (select-one [(s/sorted reverse-comparator) s/FIRST] initial-list))))))
(testing "the sorted-by navigator with keypath"
(let [initial-list [{:a 3} {:a 4} {:a 2} {:a 1}]]
(is (= (sort-by :a initial-list)
(select-one (s/sorted-by :a) initial-list)))
(is (= {:a 4} (select-one [(s/sorted-by :a (comp - compare)) s/FIRST] initial-list))))))