tolgap
tolgap

Reputation: 9778

Filter a collection by multiple attributes provided with a map

I have a collection that I want to filter. The filter is done using a map where the key is the attribute in a collection item, and the value is what the collection items should match. Example:

(let [filters {:name "test"
               :type "new"}
      collection [{:name "testable"  :type "old"}
                  {:name "shoudwork" :type "new"}
                  {:name "testable"  :type "new"}]])

Right now, I have built my filter function to only take a single attribute and value. I want to expand it to actually be able to take a filter hashmap.

This is what my current filter looks like:

(filter #(re-find (re-pattern "test") (string/lower-case (% :name))) collection)

In other words, I want the filter to not take "test" hardcoded, but it should be the value from the filters let binding, and the (% :name) to not be hardcoded to :name, but be the key from filters let binding.

Upvotes: 2

Views: 540

Answers (2)

leetwinski
leetwinski

Reputation: 17849

you can also use transducers:

;; first you create a filtering step factory for transduction:
(defn make-filter [[k v]]
  (filter #(re-find (re-pattern v)
                    (clojure.string/lower-case (k %)))))

;; and then transform the collection:
(let [filters {:name "test"
               :type "new"}
      collection [{:name "testable"  :type "old"}
                  {:name "shoudwork" :type "new"}
                  {:name "testable"  :type "new"}]]
  (sequence (reduce comp (map make-filter filters)) collection))

Upvotes: 3

DanLebrero
DanLebrero

Reputation: 8591

Create a function that returns the filtering function for a single [key expected-value]:

(defn regex-value [[k expected-rex]]
  (fn [c] (re-find (re-pattern expected-rex) 
                   (clojure.string/lower-case (get c k)))))

Create a function that will create the compounded filtering condition:

(defn build-filter [filters]
  (apply every-pred (map regex-value filters)))

Use it:

(let [filters {:name "test"
               :type "new"}
      collection [{:name "testable"  :type "old"}
                  {:name "shoudwork" :type "new"}
                  {:name "testable"  :type "new"}]]
  (filter (build-filter filters) collection))

Upvotes: 3

Related Questions