diff --git a/src/common/datomish/util.cljc b/src/common/datomish/util.cljc index d83e8e4e..c954b150 100644 --- a/src/common/datomish/util.cljc +++ b/src/common/datomish/util.cljc @@ -174,3 +174,22 @@ (let [[k v] (f x)] (assoc! ret k (conj (get ret k []) v)))) (transient {}) coll))) + +(defn repeated-keys + "Takes a seq of maps. + Returns the set of keys that appear in more than one map." + [maps] + (if (not (seq (rest maps))) + #{} + ;; This is a perfect use case for transients, except that + ;; you can't use them for intersection due to CLJ-700. + ;; http://dev.clojure.org/jira/browse/CLJ-700 + (loop [overlapping #{} + seen #{} + key-sets (map (comp set keys) maps)] + (if-let [ks (first key-sets)] + (let [overlap (clojure.set/intersection seen ks)] + (recur (clojure.set/union overlapping overlap) + (clojure.set/union seen ks) + (rest key-sets))) + overlapping)))) diff --git a/test/datomish/util_test.cljc b/test/datomish/util_test.cljc index f1e215b2..1ad00297 100644 --- a/test/datomish/util_test.cljc +++ b/test/datomish/util_test.cljc @@ -51,3 +51,18 @@ (are [m xs] (= m (util/group-by-kv identity xs)) {:a [1 2] :b [3]} [[:a 1] [:a 2] [:b 3]])) + +(deftest test-repeated-keys + (let [abc {:a 1 :b 2 :c 3} + def {:d 1 :e 2 :f 3} + bcd {:b 1 :c 2 :d 3} + efg {:e 1 :f 2 :g 3} + empty {}] + (is (= #{} (util/repeated-keys []))) + (is (= #{} (util/repeated-keys [empty]))) + (is (= #{} (util/repeated-keys [empty empty]))) + (is (= #{} (util/repeated-keys [abc empty empty]))) + (is (= #{} (util/repeated-keys [abc def empty]))) + (is (= #{:b :c} (util/repeated-keys [bcd abc]))) + (is (= #{:b :c :d} (util/repeated-keys [abc def bcd]))) + (is (= #{:b :c :d :e :f :g} (util/repeated-keys [abc efg def efg bcd])))))