指定一个集合,使其恰好具有每个值之一

Spec a collection to have exactly one of each values

有这张地图:

{:id 5
 :fields [{:field :a :val 1}
          {:field :c :val 3}
          {:field :b :val 2}
          {:field :d :val 4}
          ...]}

和每个字段值的单独规范,如 (s/def ::field-a ...)(s/def ::field-b ...)(s/def ::field-c ...)

我应该如何指定它以便地图中的 :fields 包含 {:field a ...}{:field b ...}{:field c ...}(无视顺序)和可选其他值?

是的,我知道它有点冗长,但无论如何:

(s/def ::id int?)
(s/def ::val int?)
(s/def ::field keyword?)
(s/def ::some string?)

(s/def ::abstract-field (s/keys :req-un [::field ::val]))

(s/def ::field-a (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :a)))
(s/def ::field-b (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :b)))
(s/def ::field-c (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :c)))
(s/def ::field-d (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :d)))

(s/def ::fields (s/and
                    (s/+ (s/alt :required (s/or :a ::field-a
                                                :b ::field-b
                                                :c ::field-c
                                                :d ::field-d)
                                :rest ::abstract-field))
                    (fn [x] (= [:a :b :c :d]
                               (into [] (sort (map (comp first second) (filter #(= :required (first %)) x))))))))

(let [x1 [{:field :b :val 2}
          {:field :a :val 1}
          ;{:field :a :val 1}
          {:field :d :val 4}
          {:field :e :val 4}
          {:field :c :val 3}
          ]]
    (s/explain ::fields x1)
    (s/conform ::fields x1))