Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save danielneal/cde96234f55fff2d1392e109bf383586 to your computer and use it in GitHub Desktop.
Save danielneal/cde96234f55fff2d1392e109bf383586 to your computer and use it in GitHub Desktop.
compound
(ns compound.core
(:refer-clojure :exclude [get-in update-in assoc-in]))
(def ^:private index-permutations
(memoize
(fn permutations [coll]
(if (= 1 (count coll))
(list coll)
(for [head coll
tail (permutations (disj (set coll) head))]
(cons head tail))))))
(defn compound [coll compound-index]
(let [indexes (index-permutations compound-index)
updates (for [index indexes
item coll]
[(mapv (fn [k]
(if-let [idx (get item k)]
{k idx}
(throw (ex-info "key-fn returns nil" {:key k
:item item
:index index})))) index) item])]
{:indexes (set indexes)
:index-length (count compound-index)
:data (reduce (fn [m [k v]]
(let [existing-value (clojure.core/get-in m k)]
(if existing-value
(throw (ex-info "Duplicate key" {:key k
:new-value v
:existing-value existing-value}))
(clojure.core/assoc-in m k v)))) {} updates)}))
(def c (compound #{{:product-id 1 :date "today" :customer-id 1}
{:product-id 2 :date "tomorrow" :customer-id 2}
{:product-id 1 :date "tomorrow" :customer-id 3}
{:product-id 1 :date "tomorrow" :customer-id 4}}
[:product-id :date :customer-id]))
(defn get-in [compound ks]
(let [{:keys [indexes index-length data]} compound
freedom (- index-length (count ks))
root (clojure.core/get-in data ks)]
(if (= freedom 0)
root
(nth (iterate #(mapcat vals %) (vals root)) (dec freedom)))))
(get-in c [{:date "tomorrow"} {:product-id 1}])
(defn update-in [compound ks f & args]
(let [{:keys [indexes index-length data]} compound
freedom (- index-length (count ks))
root (clojure.core/get-in data ks)]
(if (= freedom 0)
root
(nth (iterate #(mapcat vals %) (vals root)) (dec freedom)))))
(defn update-all-indexes [compound ks f]
(let [new-value (f (get-in compound ks))
compound (assoc-in compound ks new-value)
other-indexes (disj (set (index-permutations ks)) ks)]
(reduce (fn [compound index] (assoc-in compound index new-value)) compound other-indexes)))
(defn update-in [compound ks f & args]
(update-all-indexes update-in))
(update-all-indexes c [{:date "tomorrow"} {:product-id 2}] (fn [v] (clojure.core/update v :date str "a")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment