subseq

added
1.0

ns
clojure.core

type
function

(subseq sc test key) (subseq sc start-test start-key end-test end-key)

sc must be a sorted collection, test(s) one of <, <=, > or
>=. Returns a seq of those entries with keys ek for
which (test (.. sc comparator (compare ek key)) 0) is true

                ;; Note, that collection passed to subseq must implement Sorted.  
;; Just passing a collection that has been sorted is not enough.

user=> (subseq [1 2 3 4] > 2)
java.lang.ClassCastException: clojure.lang.PersistentVector cannot be cast to clojure.lang.Sorted (NO_SOURCE_FILE:0)

user=> (subseq (sorted-set 1 2 3 4) > 2)
(3 4)

            
                ;; Example of getting a subsequence of hashmaps sorted by key :a and
;;  secondarily :b.

(defn compare-ab [x y]
  (compare [(get x :a) (get x :b)]
           [(get y :a) (get y :b)]))
 
(def ss-ab (apply sorted-set-by compare-ab
                      [{:a 42 :b 5000}
                       {:a 1 :b 2}
                       {:a 99 :b -1000}
                       {:a -1 :b 7}]))
user=> ss-ab
#{{:a -1, :b 7} {:a 1, :b 2} {:a 42, :b 5000} {:a 99, :b -1000}}

;; Select all maps whose key :a is greater than 5. 
user=> (subseq ss-ab > {:a 5})
({:a 42, :b 5000} {:a 99, :b -1000})



            
                ;; If you use the 6 input form of this function, start-test should be > or
;; >= and the end-test should be < or <=.  The other forms don't give you
;; an error, but don't give you what you expect, either.  This is all based
;; on experimentation.  I don't see this documented.

;; This suggests that there are no items in my set between 9 and 2.
user=> (subseq (sorted-set 1 2 3 4 5 6 7 8 9 0) < 9 > 2)
;; => ()

;; This correctly lists all items in my set between 2 and 9.
user=> (subseq (sorted-set 1 2 3 4 5 6 7 8 9 0) > 2 < 9)
;; => (3 4 5 6 7 8)

;; Again, this is not just the "and" of the two conditions.  Lots of items
;; in my set are #(and (% > 2) (% > 6)) but this returns nothing.  "> 2"
;; means skip to the first item that is #(% > 2).  So we jump directly to 3.
;; "> 6" means to stop looking as soon as we find an item where #(% > 6) is
;; false.  3 <= 6, so we get the empty sequence.
user=> (subseq (sorted-set 1 2 3 4 5 6 7 8 9 0) > 2  > 6)
;; => ()

;; This works as expected, returning everything where #(and (% >= 2) (% <= 4)).
;; That is to say it returns everything between 2 and 4, inclusive.
user=> (subseq (sorted-set 1 2 3 4 5 6 7 8 9 0) >= 2 <= 4)
;; => (2 3 4)

;; Naïvely you might expect this to give you the same results as the previous
;; statement.  Clearly the result is not the same.  I'm not sure what's going
;; on under the hood here.  It's jumping directly to 4 as if I'd said ">= 4"
;; rather than "<= 4".  (Looks like a bug in Clojure to me!)  Then it
;; continued to the end because all of the remaining items were #(% >= 2).
user=> (subseq (sorted-set 1 2 3 4 5 6 7 8 9 0) <= 4 >= 2)
;; => (4 5 6 7 8 9)
user=> *clojure-version*
;; => {:major 1, :minor 8, :incremental 0, :qualifier nil}

;; This one at least makes sense.  It jumps directly to the first item that
;; satisfies the first test and continues until the second item is false.
user=> (subseq (sorted-set 1 2 3 4 5 6 7 8 9 0) >= 4 >= 2)
;; => (4 5 6 7 8 9)