Brave book chapter4 and stuck

Im stuck at chapter4
so far I have this :

; Write a function, validate, which will check that :name and :glitter-index are
; present when you append. The validate function should accept two arguments:
; a map of keywords to validating functions, similar to conversions, and the
; record to be validated

(def validation {:name validate-name
                  :glitter-index validate-index})

(defn validate [validations suspects]
  ((get validations suspects)))

am I on the right path here ?

I assume validate-name and validate-index are functions that return a boolean (true if the value is valid, false if not).

From the requirements of validate it appears that the second parameter to it should be a single suspect, not “suspects” (plural).

What you now want is to extract the value of each key (mentioned in validations) from suspect and then call the corresponding validation function on that value. Then you want to check that all of the validators return true.

Here is one way of defining a key-validator.

(defn key-validator [validators key suspect]
  ;; First verify that the key exists in the suspect
  ;; Then call the validator of the given key on the value of the key in suspect
  (and (get suspect key) ((get validators key) (get suspect key))))

This assumes:

  1. key is a valid key in validators.
  2. nil is not a valid value.

Hope this helps to move you forward.

This might be a good case for using when-let or if-let…

oke,

But why do I still get a unresolved symbol here

(def validation {:name validate-name
                 :glitter-index validate-index})

(defn validate
  [validators suspect]
   (and (get suspect key) ((get validators key) (get suspect key))))

(defn validate-name [name]
  (seq? name))

 (defn validate-index [index]
  (seq? index))

Clojure’s compiler is single pass, if you want to use a function before defining it, you should declare it. In this case, you could just move the defns upward to before they’re used.

You also have not added key to your argument list, that can be part of the problem.

(defn validate-name [name]
  (string? name))

 (defn validate-index [index]
  (number? index))

(defn validate [validators key suspect]
  (when-let [value (get suspect key)]
    ((get validators (constantly nil)) value)))

(def validators {:name validate-name
                 :glitter-index validate-index})

(validate validators :name {:name "toto"})
;; => true

(validate validators :name 14)
;; => nil

Thanks,

I will then have to experiment with this because both are not allowed to be empty so in your first example the a answer must be false because the glitter-index is missing.

Then you just need to change your validation functions. You could do (and (string? name) (< 0 (count name))) for example. The interesting bit is the validate function itself.

If you want to validate for a set of keys, you basically need to run the validate function for each key.

Also, sorry I had a missing key in the validate fn above.

(defn validate [validators key suspect]
  (when-let [value (get suspect key)]
    ((get validators key (constantly nil)) value)))


(defn validate-multiple [validators keys-to-test suspect]
  (reduce (fn[_ e] (if (validate validators e suspect)
                    true
                    (reduced false))) true keys-to-test))

(validate-multiple validators [:name :glitter-index] {:name "toto" :glitter-index 3})
;; => true


(validate-multiple validators [:name :glitter-index] {:name "toto" :flutter-index 3})
;; => false

Thanks,

This looks more complicated.
Maybe back to the drawing board to look for a simpler way to finisch this challenge.

hmm.

I was more thinking like this :

(defn validate
  [validators suspect]
    (every?  #(contains? validators %) suspect))


  
(validate [:name] {:name "toto"})

but this one gives false where I expect to see true

There you’re iterating over a map, which will give you 2-tuples for your function, so it’s unlikely that it’s doing what you want. The solution I gave you checks that the keys are present, and that the values are valid according to the validator functions you have defined… not sure how much simpler you can have it. You still need to iterate over the keys you want to test, and test that a) the “suspect” object contains the key, and b) that the value is valid according to your validator.

What part are you finding difficult to grasp?

First I do understand how this works :

(defn validate-multiple [validators keys-to-test suspect]
  (reduce (fn[_ e] (if (validate validators e suspect)
                    true
                    (reduced false))) true keys-to-test))

I only did now 2 chapters of the brave book

and secondly I never learned the constatnly function

Ok, let’s go by parts.
constantly returns a function that constantly returns whatever argument you passed, regardless of what value you pass as arguments. So if you do (def constant-fn (constantly 4)), any calls to constant-fn will return 4, regardless of what you pass to it.

reduce abstracts an important recursion pattern, which is that of “collapsing” the values of a collection into a single value. In this case, it takes the collection of keys you want to test, and checks whether they are al valid in the suspect object. If one of them isn’t, it will end early (by calling reduced) and return false. You could do something similar calling (every? some?... ) on the result of (map (fn[k] (validate validators k suspect)) keys-to-test).

is what youy do the same as I did here :

(defn validate
  [validators suspect]
    (every?  #((get validators %) (get suspect %)) (keys validators)))


(defn validate-name[name]
  (print name)
  (and (string? name) (> (count name) 3)))

(validate {:name validate-name} {:name nil})

True. when-let would be helpful here. I was trying to avoid any forms that were not in the chapter.

oke,

and I try to rewrite my code with reduce-ky
but whatever I try I get a function instead of a outcome

(defn validate
  [validators suspect]
    (every?  #((get validators %) (get suspect %)) (keys validators)))

(defn validate2
  [validators suspect]
  (reduce-kv (fn [vl k v] #(v (k (vl keys) (keys vl)  ) )) {} suspect)) 
 
 (defn validate-name[name]
  (print name)
  (and (string? name) (> (count name) 3)))

(validate2 {:name validate-name} {:name "aaaa"})

I’m not entirely sure what you’re trying to do here:

(defn validate2
  [validators suspect]
  (reduce-kv (fn [vl k v] #(v (k (vl keys) (keys vl)  ) )) {} suspect)) 

But you are returning a function from there (the # is creating a lambda)…

That inner form doesn’t seem to make much sense, to me.

oke
Maybe I have to write out what im thinkimg

This is working

(every?  #((get validators %) (get suspect %)) (keys validators)))

now I have this part

reduce-kv (fn [vl k v]

where
vl are the validations
k is the key which I want to check for
v is the value so the function that needs to be called to validate the function

so I have this:

#((get validators %) (get suspect %)) (keys validators))`

#((get validators %) would then be (get vl %)
an (get suspect %) would get me the value of a key of suspect so that would be the v
so together It would be (#(get vl % v)

but some how that is not working so I make somewhere a thinking error but I do not see where

Perhaps the thing that is confusing is that the anonymous function that is used with every? is not the same function that should be used with reduce-kv.

The reduce function is calling the validator (as you have), but then it has also to check the return value and if it is false, it should return false from now on. For that reason, the initial value for the reduce-kv needs to be a boolean (not a map) - the value that would be returned if there were no elements in the collection passed to reduce-kv.

little bit confused
Do this mean this :


(defn validate2
  [validators suspect]
  (reduce-kv (fn [vl k v] #(v (k (vl keys) (keys vl) ) ) true) false suspect)) 
 
 (defn validate-name[name]
  (print name)
  (and (string? name) (> (count name) 3)))

(validate2 {:name validate-name} {:name "a"})

gives true where I expect false