Assume we have a data-structure like this:(def data     (atom [{:id 1 :first-name John1 :last-name Dow1 :age 14}            {:id 2 :first-name John2 :last-name Dow2 :age 54}            {:id 3 :first-name John3 :last-name Dow3 :age 34}            {:id 4 :first-name John4 :last-name Dow4 :age 12}            {:id 5 :first-name John5 :last-name Dow5 :age 24}]))I want to filter it by specific keys using regex in predicates, so I ended up with this function which works fine, but I guess code is ugly and duplicated. How can I get rid of code duplication?(defn my-filter [str-input]  (let [firstname (filter #(re-find (->> (str str-input)                                         (upper-case)                                         (re-pattern))                                    (upper-case (:first-name %)))                     @data)        lastname (filter #(re-find (->> (str str-input)                                        (upper-case)                                        (re-pattern))                                   (upper-case (:last-name %)))                    @data)        age (filter #(re-find (->> (str str-input)                                   (upper-case)                                   (re-pattern))                              (upper-case (:age %)))               @data)]    (if-not (empty? firstname)      firstname      (if-not (empty? lastname)        lastname        (if-not (empty? age)          age)))))
Filtering a data structure using regex predicates
regex;clojure;clojurescript
First up, notice that you're creating 3 leted names by using exactly the same expression but replacing one key lookup function. This is a prime candidate for deduplication by making a local function. (defn my-filter [str-input]  (let [seek (fn [k](filter #(re-find (->> (str str-input)                                         (upper-case)                                         (re-pattern))                                    (upper-case (k %)))                     @data))         firstname (seek :first-name)        lastname (seek :last-name)        age (seek :age)]    (if-not (empty? firstname)      firstname      (if-not (empty? lastname)        lastname        (if-not (empty? age)          age)))))Next we can notice that the remainder is just doing the same return this if it's not empty logic with each of the three keys in sequence. some returns the first non-falsy result of a function on a sequence so by adding a seq to the seek function we can turn empty filter sequences into nils we can combine the two and get the logic we want. I used letfn this time because we're only leting fns, might as well.(defn my-filter [str-input]  (letfn[(seek [k]               (seq (filter #(re-find (->> (str str-input)                                         (upper-case)                                         (re-pattern))                                    (upper-case (k %)))                     @data)))]    (some seek [:first-name :last-name :age])))