Alpha A custom Clojure/Script type for custom comparator sets.
[org.martinklepsch/cc-set "0.1.2"] ;; latest release
When dealing with domain data in maps this data often has unique identifiers. To make merging easy and guarantee that there are no two items with the same identifier people often store those models in maps of the following format:
{(:id model) model
,,,}
While this certainly is a solution it complicates the handling of the
data in every place imaginable. Often you just want (vals my-models)
instead of the actual map. Adding new items requires code that is
tightly coupled to the underlying map that you only chose to guarantee
uniqueness of a certain property ((assoc my-models (:id model) model)
).
This library provides a custom type CustomComparatorSet
which allows
users to manage data in a set-like datastructure while using a custom
function as uniqueness comparator.
;; (import [org.martinklepsch.cc_set.impl CustomComparatorSet]) ; Clojure
;; (require '[org.martinklepsch.cc-set :refer [custom-comparator-set]]) ; ClojureScript
(def x (custom-comparator-set :id))
(conj x {:id 1} {:id 2})
;; => #CustomComparatorSet{{:id 1} {:id 2}}
While a keyword
:id
is used as comparator here you can also use more complex functions.
A constructor set-by
is provided which additionally wraps the passed
comparator in a nil check, throwing if nil
is returned.
(require '[org.martinklepsch.cc-set :refer [set-by]])
(set-by :id {:id "a"} {:id "x"} {:id "b"})
If you don't want the nil
guard behavior you can implement your own constructor like this:
(defn my-set-by [comparator & keys]
(reduce conj (CustomComparatorSet. {} comparator) keys)))
Simple!
Clojure
(import [org.martinklepsch.cc_set.impl CustomComparatorSet])
(CustomComparatorSet. {} :id)
ClojureScript
(require '[org.martinklepsch.cc-set.impl :refer [CustomComparatorSet]])
(CustomComparatorSet. nil {} :id)
Notice the different arity, resulting of the differences in how Clojure and ClojureScript handle metadata.
get
Regular set: (get regular-set v)
looks for entries where v == entry
Custom-comparator set: (get ccset v)
look for entries where (comparator v) == (comparator entry)
conj
Regular set: (conj regular-set v)
will add the value only if the set does not already contain it, maintaining uniqueness by value equality
Custom-comparator set: (conj ccset v)
will behave like assoc
with the key (comparator v)
and the value v
, maintaining uniqueness by comparator. This means that values will be updated if the comparator result is equal but the value is different
boot build-jar # install to ~/.m2
boot testing test-cljc # run tests once
boot testing watch test-cljc # run tests after changes
Feedback and PRs welcome, especially generative testing seems like a good fit.
--
MPLv2, see LICENSE
.