How do I test the true/false of several expressions?

Assume I have vectors like this:

[(= :land :land) (= :good :good)]

If I do:

(every? true? [(= :land :land) (= :good :good)])

I get “true”.

If I do:

(every? true? [(= :land :land) (= :good :green)])

I get “false”.

This is fine. But I’m trying to build this vector of expressions in this function:

(defn make-query
[item f]
(loop [
k (keys f)
query
]
(if (nil? (first k))
query
(recur
(rest k)
(conj query `(= ~(get item (first k)) ~(get f (first k))))))))

This is where things get strange.

Let’s say I do this in the repl:

(def item {:type :land :status :good})

If I do:

(make-query item {:type :land :status :good})

then it seems to return:

[(clojure.core/= :land :land) (clojure.core/= :good :good)]

But if I do this:

(every? true? (make-query item {:type :land :status :good}))

I get:

false

What did I do wrong?

As background, I should explain I’m actually just trying to create something I can use as a predicate for a call to filterv. I’ve a vector full of maps, and I need a flexible way to query what is in the vector. I was actually hoping to specify the matches in a separate configuration file. But to get a flexible system, I need to get something like make-query to work.

Assume with filterv I’d use a predicate like

(fn (every? true? (make-query x f)))

I tried several things with quotes and unquote splicing but I could not get any of it to work. I was thinking I could return a list expressions and then somehow use that with the “and” macro but I could not get the syntax correct.

But let’s stick with the current question. Why doesn’t (every? true?) work on the vector returned from (make-query)?

A few more experiments at the REPL:

(first (make-query item {:type :land :status :good}))

returns:

(clojure.core/= :land :land)

but this:

(true? (first (make-query item {:type :land :status :good})))

returns:

false

but this:

(clojure.core/= :land :land)

returns:

true

UPDATE:

Hmm, okay, using “eval” seems to help:

(true? (eval (first (make-query item {:type :land :status :good}))))

returns true

what did I do wrong?

;;formatted for clarity.
(defn make-query [item f]
  (loop [k (keys f)
         query []]
    (if (nil? (first k))
      query
      (recur
       (rest k)
       (conj query `(= ~(get item (first k)) ~(get f (first k))))))))

(make-query item {:type :land :status :good})
;;yields  [(clojure.core/= :land :land) (clojure.core/= :good :good)])

(every? true? (make-query item {:type :land :status :good}))
;;equivalent to 
(every? true? [(clojure.core/= :land :land) (clojure.core/= :good :good)])

make-query is returning a vector of unevaluated expressions. None of them are identical to true, which is the criteria that true? uses (it’s specifically looking for true boolean values, not the wider notion of “truthy” which includes anything that is not nil or false). The results will always be false.

This works and seems adjacent to what you’re approaching.

(defn simple-query [desired]
  (fn [m]
    (->> desired
         keys
         (every? (fn [k] (= (m k) (desired k)))))))

;;more efficient using reduce-kv
;; (defn simple-query [desired]
;;   (fn [m]
;;     (reduce-kv (fn [acc k v]
;;                  (if (= (m k) v)
;;                    acc
;;                    (reduced false))) true desired)))

(def some-data
  [{:id 0 :type :land :status :bad }
   {:id 1 :type :hobbit :name :baggins}
   {:id 2 :type :land :status :good}])

(->> some-data
     (filterv (simple-query {:type :land :status :good})))

;;[{:id 2, :type :land, :status :good}]
1 Like

Thank you so much!

Hello guys! :heart_eyes:

As per my knowledge, You can use eval to evaluate them:

(true? (eval (first (make-query item {:type :land :status :good}))))

And Alternatively, modify make-query to return boolean values:

(defn make-query [item f]
  (map #(= (get item %) (get f %)) (keys f)))

I hope this will be helpful for you. :blush:

Thanks in advance! :smiling_face_with_three_hearts:

1 Like