clojure.data.clj Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of clojure Show documentation
Show all versions of clojure Show documentation
Clojure core environment and runtime library.
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns
^{:author "Stuart Halloway",
:doc "Non-core data functions."}
clojure.data
(:require [clojure.set :as set]))
(declare diff)
(defn- atom-diff
"Internal helper for diff."
[a b]
(if (= a b) [nil nil a] [a b nil]))
;; for big things a sparse vector class would be better
(defn- vectorize
"Convert an associative-by-numeric-index collection into
an equivalent vector, with nil for any missing keys"
[m]
(when (seq m)
(reduce
(fn [result [k v]] (assoc result k v))
(vec (repeat (apply max (keys m)) nil))
m)))
(defn- diff-associative-key
"Diff associative things a and b, comparing only the key k."
[a b k]
(let [va (get a k)
vb (get b k)
[a* b* ab] (diff va vb)
in-a (contains? a k)
in-b (contains? b k)
same (and in-a in-b
(or (not (nil? ab))
(and (nil? va) (nil? vb))))]
[(when (and in-a (or (not (nil? a*)) (not same))) {k a*})
(when (and in-b (or (not (nil? b*)) (not same))) {k b*})
(when same {k ab})
]))
(defn- diff-associative
"Diff associative things a and b, comparing only keys in ks."
[a b ks]
(reduce
(fn [diff1 diff2]
(doall (map merge diff1 diff2)))
[nil nil nil]
(map
(partial diff-associative-key a b)
ks)))
(defn- diff-sequential
[a b]
(vec (map vectorize (diff-associative
(if (vector? a) a (vec a))
(if (vector? b) b (vec b))
(range (max (count a) (count b)))))))
(defprotocol ^{:added "1.3"} EqualityPartition
"Implementation detail. Subject to change."
(^{:added "1.3"} equality-partition [x] "Implementation detail. Subject to change."))
(defprotocol ^{:added "1.3"} Diff
"Implementation detail. Subject to change."
(^{:added "1.3"} diff-similar [a b] "Implementation detail. Subject to change."))
(extend nil
Diff
{:diff-similar atom-diff})
(extend Object
Diff
{:diff-similar (fn [a b] ((if (.. a getClass isArray) diff-sequential atom-diff) a b))}
EqualityPartition
{:equality-partition (fn [x] (if (.. x getClass isArray) :sequential :atom))})
(extend-protocol EqualityPartition
nil
(equality-partition [x] :atom)
java.util.Set
(equality-partition [x] :set)
java.util.List
(equality-partition [x] :sequential)
java.util.Map
(equality-partition [x] :map))
(defn- as-set-value
[s]
(if (set? s) s (into #{} s)))
(extend-protocol Diff
java.util.Set
(diff-similar
[a b]
(let [aval (as-set-value a)
bval (as-set-value b)]
[(not-empty (set/difference aval bval))
(not-empty (set/difference bval aval))
(not-empty (set/intersection aval bval))]))
java.util.List
(diff-similar [a b]
(diff-sequential a b))
java.util.Map
(diff-similar [a b]
(diff-associative a b (set/union (keys a) (keys b)))))
(defn diff
"Recursively compares a and b, returning a tuple of
[things-only-in-a things-only-in-b things-in-both].
Comparison rules:
* For equal a and b, return [nil nil a].
* Maps are subdiffed where keys match and values differ.
* Sets are never subdiffed.
* All sequential things are treated as associative collections
by their indexes, with results returned as vectors.
* Everything else (including strings!) is treated as
an atom and compared for equality."
{:added "1.3"}
[a b]
(if (= a b)
[nil nil a]
(if (= (equality-partition a) (equality-partition b))
(diff-similar a b)
(atom-diff a b))))
© 2015 - 2025 Weber Informatics LLC | Privacy Policy