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
|
:boundary-type :end
|
||||||
:type (range-type range)}]))
|
:type (range-type range)}]))
|
||||||
(sort-by (juxt :value (comp {:start 0 :end 1} :boundary-type)))))
|
(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))
|
(assert (<= start end))
|
||||||
(->IntInclusiveDiscreteValueRange 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
|
(deftest integer-ranges-sanity-test
|
||||||
(testing "value equality"
|
(testing "value equality"
|
||||||
(is (= (int-range-inclusive 0 1)
|
(is (= (int-range-inclusive 0 1)
|
||||||
@@ -54,15 +57,19 @@
|
|||||||
(testing "end"
|
(testing "end"
|
||||||
(is (= 8 (range/end (int-range-inclusive 1 8))))))
|
(is (= 8 (range/end (int-range-inclusive 1 8))))))
|
||||||
|
|
||||||
|
;; test against integer ranges for easy of expression and interpretation
|
||||||
(deftest ordered-range-values
|
(deftest ordered-range-values
|
||||||
(testing "ordered range values are sorted by value and then the range's start boundary before any end boundary"
|
(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 :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 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}]
|
{: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 8)
|
||||||
(int-range-inclusive 5 5)])))
|
(int-range-inclusive 5 5)])))
|
||||||
(is (= [{:value 5 :boundary-type :start :type :int-range-inclusive}
|
(is (= [{:value 5 :boundary-type :start :type :int-range-inclusive}
|
||||||
@@ -71,3 +78,25 @@
|
|||||||
{:value 5 :boundary-type :end :type :int-range-inclusive}]
|
{:value 5 :boundary-type :end :type :int-range-inclusive}]
|
||||||
(#'range/ordered-range-values [(int-range-inclusive 5 5)
|
(#'range/ordered-range-values [(int-range-inclusive 5 5)
|
||||||
(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