clj-schema provides a function, clj-schema.schema/simple-schema
, that can be used to turn arbitrary predicates into schemas. Here's how to use it to implement a maps-with-unique-key?
schema:
(defn maps-with-unique-key? [k]
(s/simple-schema [(s/sequence-of map?)
(fn [xs]
(= (count xs)
(count (distinct (map #(get % k) xs)))))]))
At the REPL:
(v/valid? (maps-with-unique-key? :id)
[])
;= true
(v/valid? (maps-with-unique-key? :id)
[{:id 0 :foo "bar"} {:id 1 :foo "baz"} {:id 2 :foo "quux"}])
;= true
(v/valid? (maps-with-unique-key? :id)
[{:id 0 :foo "bar"} {:id 1 :foo "baz"} {:id 0 :foo "quux"}])
;= false
(v/valid? (maps-with-unique-key? :id)
[["not a map"] {:id 0 :foo "bar"} {:id 1 :foo "baz"} {:id 2 :foo "quux"}])
;= false
(What follows is my original answer for Prismatic schema.)
I don't think there's a ready-made schema for this in the standard schema distribution, but it's always possible to implement a new one -- see the Defining New Schema Types page in schema's wiki.
Here's a sketch:
(defrecord MapsWithUniqueKey [k]
s/Schema
(walker [this]
(fn [x]
(if (and (or (seq? x) (vector? x))
(every? map? x)
(every? #(contains? % k) x)
(== (count x)
(count (distinct (map #(get % k) x)))))
x
(schema.macros/validation-error
this x
(list 'maps-with-unique-key? k (schema.utils/value-name x))))))
(explain [this]
(list 'maps-with-unique-key? k)))
Example validations:
(s/check (->MapsWithUniqueKey :id)
[{:id 1 :foo "bar"} {:id 2 :foo "baz"} {:id 3 :foo "quux"}])
;= nil
(s/check (->MapsWithUniqueKey :id)
[{:id 1 :foo "bar"} {:id 2 :foo "baz"} {:id 1 :foo "quux"}])
;= (not (maps-with-unique-key? :id a-clojure.lang.PersistentVector))
The nil
returned from the first call indicates success, while the latter return value is a schema.utils.ValidationError
.