Skip to content

Instantly share code, notes, and snippets.

@danielneal
Created July 30, 2017 16:27
Show Gist options
  • Save danielneal/22d420823efe7a02e27ac50c7c92b7c0 to your computer and use it in GitHub Desktop.
Save danielneal/22d420823efe7a02e27ac50c7c92b7c0 to your computer and use it in GitHub Desktop.
A micro structure for your data
(ns compound.core)
(def ^:private meta-index-defs
[{:id :id
:index-fn :id
:unique? true}
{:id :unique?
:index-fn :unique?
:unique? false}])
(defn ^:private molecule [coll index-defs]
(let [initial-molecule [[] {}]
[molecule _] (reduce (fn [acc x]
(let [[molecule j] acc
[xs m] molecule]
[[(conj xs x)
(reduce (fn [m index-def]
(let [{:keys [index-fn unique? id]} index-def
k (index-fn x)]
(cond (and (some? k) unique?)
(let [existing (get-in m [id k])]
(if existing
(throw (ex-info "Duplicate key " {:x x
:k k}))
(assoc-in m [id k] j)))
(and (some? k) (not unique?))
(update-in m [id k] (fnil conj #{}) j)
:else m)))
m index-defs)]
(inc j)]))
[initial-molecule 0]
coll)]
molecule))
(defn ^:private molecule-get-positions [molecule k v]
(let [[_ m] molecule]
(get-in m [k v])))
(def ^:private molecule-get-position molecule-get-positions)
(defn ^:private molecule-get-element [molecule j]
(let [[xs _] molecule]
(get xs j)))
(defn ^:private molecule-get-elements [molecule js]
(let [[xs _] molecule]
(into #{} (map #(get xs %)) js)))
(defn ^:private molecule-get-single [molecule k v]
(->> (molecule-get-position molecule k v)
(molecule-get-element molecule)))
(defn ^:private molecule-get-many [molecule k v]
(->> (molecule-get-positions molecule k v)
(molecule-get-elements molecule)))
(defn compound [coll index-defs]
[(molecule coll index-defs)
(molecule index-defs meta-index-defs)])
(defn compound-get [compound k v]
(let [[molecule metacule] compound
index (molecule-get-single metacule :id k)]
(if (nil? index)
(throw (ex-info (str "No index on " k " present") {:k k, :v v :compound compound}))
(if (:unique? index)
(molecule-get-single molecule k v)
(molecule-get-many molecule k v)))))
(defn ^:private compound-update-single [compound k v f & args]
(let [[molecule metacule] compound
index (molecule-get-single metacule :id k)
{:keys [index-fn]} index
j (molecule-get-position molecule k v)
existing-value (molecule-get-element molecule j)
new-value (apply f existing-value args)
[indexes _] metacule
[xs m] molecule
new-molecule (reduce (fn [molecule index]
(let [[xs m] molecule
{:keys [unique? index-fn id]} index
old-v (if (= id k) v (index-fn existing-value))
new-v (index-fn new-value)]
[xs (cond
(and (not= old-v new-v) (nil? new-v) unique?) (update m k dissoc old-v)
(and (not= old-v new-v) (not (nil? new-v)) unique?) (if-let [existing-value (get-in m [id new-v])]
(throw (ex-info "Duplicate key" {:id id :v new-v}))
(-> m (update id dissoc old-v) (update id assoc new-v j)))
(and (not= old-v new-v) (nil? new-v) (not unique?)) (-> m (update-in [id old-v] disj j))
(and (not= old-v new-v) (not (nil? new-v)) (not unique?)) (-> m (update-in [id old-v] disj j) (update-in [id new-v] (fnil conj #{}) j))
:else m)]))
[(assoc xs j new-value) m] indexes)]
[new-molecule
metacule]))
#_(defn ^:private compound-update-many [compound k v f & args]
(let [[molecule metacule] compound
index (molecule-get-single metacule :id k)
[indexes _] metacule
existing-values (molecule-get-many k v)
new-values (into #{} (apply f existing-values args))
initial-molecule (reduce (fn [molecule index])
molecule )
new-molecule (reduce (fn [molecule index]
(let [[xs m] molecule
{:keys [unique? index-fn id]} index
old-v (if (= id k) v (index-fn existing-value))
new-v (index-fn new-value)]
[xs (cond
(and (not= old-v new-v) (nil? new-v) unique?) (update m k dissoc old-v)
(and (not= old-v new-v) (not (nil? new-v)) unique?) (if-let [existing-value (get-in m [id new-v])]
(throw (ex-info "Duplicate key" {:id id :v new-v}))
(-> m (update id dissoc old-v) (update id assoc new-v j)))
(and (not= old-v new-v) (nil? new-v) (not unique?)) (-> m (update-in [id old-v] disj j))
(and (not= old-v new-v) (not (nil? new-v)) (not unique?)) (-> m (update-in [id old-v] disj j) (update-in [id new-v] (fnil conj #{}) j))
:else m)]))
molecule indexes)]
[new-molecule
metacule]))
;; initial-molecule [(assoc molecule j new-value)
;; (cond-> m
;; (not= v new-v) (update k dissoc v)
;; (not (nil? new-v)) (update k assoc new-v j)) metacule]
(defn compound-update [compound k v f & args]
(let [[molecule metacule] compound
index (molecule-get-single metacule :id k)]
(if (:unique? index)
(apply compound-update-single compound k v f args)
(apply compound-update-many compound k v f args))))
(clojure.pprint/pprint (compound-update-single c [:a :b] [1 1] assoc :a 2 :b 2))
(def c (compound #{{:a 1 :b 2 :c 3}
{:a 1 :b 1 :c 4}}
#{{:index-fn :a
:id :a
:unique? false}
{:index-fn :b
:id :b
:unique? false}
{:index-fn (juxt :a :b)
:id [:a :b]
:unique? true}}))
(compound-get c [:a :b] [1 1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment