consolidate overlapping ranges
transduce over the ordered range values starts and ends and do the stack push/pop matching over the start and end values to consolidate the overlapping ranges and result in a new set of consolidated and sorted range values.
This commit is contained in:
@@ -52,3 +52,48 @@
|
||||
:boundary-type :end
|
||||
:type (range-type range)}]))
|
||||
(sort-by (juxt :value (comp {:start 0 :end 1} :boundary-type)))))
|
||||
|
||||
(defmulti ->discrete-value-range (fn [range-type _start _end]
|
||||
range-type))
|
||||
|
||||
(defn combine-overlapping-ranges
|
||||
"transducer to find and combine overlapping ranges by looking at
|
||||
ordered range value markers.
|
||||
|
||||
When it encounters a range's start boundary it pushes an entry on
|
||||
the stack, and captures the value for that start boundary if the
|
||||
stack was previously empty.
|
||||
|
||||
When it encounters a range's end boundary value, it pops an item
|
||||
from the stack, and if the stack is now empty, it uses the value
|
||||
of the range boundary end and the captured start value to create
|
||||
a new range object, which will contain any other range start and
|
||||
end pairs encountered between the new start and end values."
|
||||
[]
|
||||
(fn [xf]
|
||||
(let [stack (volatile! [])
|
||||
start (volatile! nil)]
|
||||
(fn
|
||||
([] (xf))
|
||||
([result] (xf result))
|
||||
([result input]
|
||||
(if (reduced? input)
|
||||
result
|
||||
(case (:boundary-type input)
|
||||
:start (do
|
||||
(when-not (seq @stack)
|
||||
(vreset! start (:value input)))
|
||||
(vswap! stack (fnil conj []) input)
|
||||
result)
|
||||
:end (do
|
||||
(vswap! stack pop)
|
||||
(if (seq @stack)
|
||||
result
|
||||
(xf result (->discrete-value-range (:type input) @start (:value input))))))))))))
|
||||
|
||||
(def consolidate-ranges-xf
|
||||
(comp
|
||||
(combine-overlapping-ranges)))
|
||||
|
||||
(defn consolidate [ranges]
|
||||
(eduction consolidate-ranges-xf (ordered-range-values ranges)))
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
(assert (<= start end))
|
||||
(->IntInclusiveDiscreteValueRange start end))
|
||||
|
||||
(defmethod range/->discrete-value-range :int-range-inclusive [_ start end]
|
||||
(int-range-inclusive start end))
|
||||
|
||||
(deftest integer-ranges-sanity-test
|
||||
(testing "value equality"
|
||||
(is (= (int-range-inclusive 0 1)
|
||||
@@ -54,15 +57,19 @@
|
||||
(testing "end"
|
||||
(is (= 8 (range/end (int-range-inclusive 1 8))))))
|
||||
|
||||
;; test against integer ranges for easy of expression and interpretation
|
||||
(deftest ordered-range-values
|
||||
(testing "ordered range values are sorted by value and then the range's start boundary before any end boundary"
|
||||
(is (= [{:value 4 :boundary-type :start :type :int-range-inclusive}
|
||||
(is (= [{:value 1 :boundary-type :start :type :int-range-inclusive}
|
||||
{:value 2 :boundary-type :end :type :int-range-inclusive}
|
||||
{:value 4 :boundary-type :start :type :int-range-inclusive}
|
||||
{:value 5 :boundary-type :start :type :int-range-inclusive}
|
||||
{:value 5 :boundary-type :start :type :int-range-inclusive}
|
||||
{:value 5 :boundary-type :end :type :int-range-inclusive}
|
||||
{:value 5 :boundary-type :end :type :int-range-inclusive}
|
||||
{:value 8 :boundary-type :end :type :int-range-inclusive}]
|
||||
(#'range/ordered-range-values [(int-range-inclusive 4 5)
|
||||
(#'range/ordered-range-values [(int-range-inclusive 1 2)
|
||||
(int-range-inclusive 4 5)
|
||||
(int-range-inclusive 5 8)
|
||||
(int-range-inclusive 5 5)])))
|
||||
(is (= [{:value 5 :boundary-type :start :type :int-range-inclusive}
|
||||
@@ -71,3 +78,25 @@
|
||||
{:value 5 :boundary-type :end :type :int-range-inclusive}]
|
||||
(#'range/ordered-range-values [(int-range-inclusive 5 5)
|
||||
(int-range-inclusive 5 5)])))))
|
||||
|
||||
;; test against integer ranges for easy of expression and interpretation
|
||||
(deftest consolidate-ranges
|
||||
(testing "combines overlapping ranges"
|
||||
(is (= [(int-range-inclusive 5 5)]
|
||||
(range/consolidate [(int-range-inclusive 5 5)
|
||||
(int-range-inclusive 5 5)])))
|
||||
(is (= [(int-range-inclusive 0 1)
|
||||
(int-range-inclusive 2 7)
|
||||
(int-range-inclusive 9 11)]
|
||||
(range/consolidate [(int-range-inclusive 0 1)
|
||||
(int-range-inclusive 2 4)
|
||||
(int-range-inclusive 3 7)
|
||||
(int-range-inclusive 5 5)
|
||||
(int-range-inclusive 9 11)
|
||||
(int-range-inclusive 5 5)])))
|
||||
(is (= [(int-range-inclusive 2 11)]
|
||||
(range/consolidate [(int-range-inclusive 2 4)
|
||||
(int-range-inclusive 3 7)
|
||||
(int-range-inclusive 5 5)
|
||||
(int-range-inclusive 6 11)
|
||||
(int-range-inclusive 5 5)])))))
|
||||
|
||||
Reference in New Issue
Block a user