Fork me on GitHub
#clojure-spec
<
2018-11-08
>
danielstockton09:11:57

Can anyone suggest how i might spec that a vector should contain maps in a particular sort order? (based on a key)

danielstockton09:11:27

It might contain any number of maps, but they should be in a particular order based on a :type key.

borkdude10:11:47

@danielcompton use this predicate?

(defn sorted-by? [coll k]
    (and (reduce (fn [prev m]
                   (if (pos? (compare (get prev k) (get m k)))
                     (reduced false)
                     m)) coll)
         true))

borkdude11:11:18

ah sorry 🙂

borkdude10:11:19

I’m not sure if predicates could give more information to spec to present a more helpful error than ::s/invalid

danielstockton10:11:13

Yeah, i'll need a different comparator but the basic idea would work. Thanks!

borkdude10:11:18

(sorted-by? [{:type 2} {:type 3} {:type 3}] :type) ;; true
(sorted-by? [{:type 2} {:type 3} {:type 2}] :type) ;; false

danielstockton10:11:41

Type is a string and the order is quite specific. But I can easily work that part out, thanks.

danielstockton10:11:20

I'll use a higher order function like

(defn correctly-sorted? [sort-order k]
  (fn [coll] ...))

danielstockton10:11:25

(defn correctly-sorted? [sort-order k]
  (fn [coll]
    (reduce
     (fn [prev m]
       (if (> (.indexOf sort-order (get prev k))
              (.indexOf sort-order (get m k)))
         (reduced false)
         m))
     coll)))
Does it matter if the returned value is simply truthy rather than true?

borkdude11:11:30

depends what you want. it’s a convention to return booleans from ? functions.

borkdude11:11:07

note that you’re using > which doesn’t work for the case when you have equal elements with regards to k

borkdude11:11:34

stylistically: correctly- is a bit redundant, it’s either sorted, or not

danielstockton11:11:06

Thanks, you're right on all fronts..

borkdude11:11:01

haha, didn’t mean to be pedantic, but couldn’t help 😉

danielstockton11:11:14

Problem is sorted? is a core function

borkdude11:11:32

sorted-by? isn’t

borkdude11:11:02

but this is just naming, choose whatever you want 😉

danielstockton11:11:22

sorted-accordingly? 😛 sorted-by? is just fine

Matt Butler11:11:30

Hi, I'm trying to spec a list of different maps, based on the docs I think I'm supposed to use a multi-spec, which i've done in the past without trouble. However I want to dispatch on more than one key/value, which I haven't seen an example of. Is this supported? I'm doing this, and valid? works correctly (returns true). But explain returns something unexpected so wanted to check im not doing something wrong

(defmulti foo
  (fn [x]
    [(:bar x) (:baz x)]))

(defmethod foo [1 2] [_]
  #{{:bar 1 :baz 2 :biff 3}})

(defmethod foo [:a :b] [_]
 #{{:bar :a :baz :b :biff :c}})

(s/explain (s/multi-spec foo :foo/type) {:bar :a :baz :b :biff :c}) => val: {:bar :a, :baz :b, :biff :c} fails at: [[:a :b]] predicate: foo
(s/valid? (s/multi-spec foo :foo/type) {:bar :a :baz :b :biff :c}) => true
I suspect its because I've just put something random :foo/type in the retag arg.

Matt Butler12:11:50

So it appears that multispec doesn't conform the set to a spec for you, which was causing the inconsistency. Returning (s/spec #{{:bar :a :baz :b :biff :c}}) from the methods, now keeps both explain and valid happy.

Matt Butler11:11:45

On a slightly unrelated note, is it considered back practice to transform data before checking it against a spec? I have the same data but in 2 different formats and i'd prefer to not to spec each one individually. I think I can write some fairly simple code to transform 1 into the other and then check it against a single spec. [a b c] => {:foo a :bar b :baz c}

alexmiller13:11:18

Doesn’t seem inherently bad to me

👍 1