Skip to content

Instantly share code, notes, and snippets.

@noprompt
Last active August 29, 2015 14:04
Show Gist options
  • Save noprompt/4ca8775a664bb5762637 to your computer and use it in GitHub Desktop.
Save noprompt/4ca8775a664bb5762637 to your computer and use it in GitHub Desktop.
Map diff based on clj-diff.
(require '[clj-diff.core :as d])
(require '[clojure.set :as set])
(defn mdiff [ma mb base-path]
(let [es (reduce
(fn [m k]
(let [vb (get mb k)
kp (conj base-path k)]
(if-let [va (get ma k)]
(if (not= va vb)
(cond
(or (and (string? va) (string? vb))
(and (sequential? va) (sequential? vb)))
(let [es (d/diff va vb)
ds (with-meta (:- es) {:patch? true})
as (with-meta (:+ es) {:patch? true})]
(-> m
(assoc-in [:- kp] ds)
(assoc-in [:+ kp] as)))
(and (map? va) (map? vb))
(let [p (mdiff va vb [k])]
(merge-with merge m p))
:else
(-> m
(assoc-in [:- kp] nil)
(assoc-in [:+ kp] vb)))
m)
(assoc-in m [:+ kp] vb))))
{}
(keys mb))
es (reduce
(fn [es k]
(assoc-in es [:- (conj base-path k)] nil))
es
(set/difference (set (keys ma)) (set (keys mb))))]
es))
(defn mpatch [m edit-script]
(let [additions (:+ edit-script)
deletions (:- edit-script)
m (reduce (fn [m [kp v]]
(let [k (peek kp)
kp' (pop kp)]
(if (nil? v)
(if (seq kp')
(update-in m kp' dissoc k)
(dissoc m k))
(let [kpv (get-in m kp)]
(if (string? kpv)
m ;; Handled in deletions :-/
(assoc-in m kp
(into (empty kpv)
(d/patch kpv {:- v}))))))))
m
deletions)
m (reduce (fn [m [kp v]]
(if (vector? v)
(let [kpv (get-in m kp)]
(cond
(string? kpv)
(assoc-in m kp (d/patch kpv {:+ v :- (get deletions kp)}))
(:patch? (meta v))
(assoc-in m kp
(into (empty kpv)
(d/patch kpv {:+ v})))
:else
(assoc-in m kp v)))
(assoc-in m kp v)))
m
additions)]
m))
;; Diff
(let [a {:x [1 2 3]
:y {:a 1 :b 2 :c 3}}
b {:x [1 4 3]
:y {:a 1 :b 5 :c 0}}
p (mdiff a b [])]
p)
;; => {:- {[:x] [1], [:y :b] nil, [:y :c] nil},
;; :+ {[:x] [[0 4]], [:y :b] 5, [:y :c] 0}}
;; Patch
(let [a {:x [1 2 3]
:y {:a 1 :b 2 :c 3}}
b {:x [1 4 3]
:y {:a 1 :b 5 :c 0}}
p (mdiff a b [])]
(mpatch a p))
;; {:y {:c 0, :b 5, :a 1},
;; :x [1 4 3]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment