Filtering a data structure using regex predicates 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 (-&gt;&gt; (str str-input)
                                         (upper-case)
                                         (re-pattern))
                                    (upper-case (:first-name %)))
                     @data)
        lastname (filter #(re-find (-&gt;&gt; (str str-input)
                                        (upper-case)
                                        (re-pattern))
                                   (upper-case (:last-name %)))
                    @data)
        age (filter #(re-find (-&gt;&gt; (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)))))
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 (-&gt;&gt; (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 (-&gt;&gt; (str str-input)
                                         (upper-case)
                                         (re-pattern))
                                    (upper-case (k %)))
                     @data)))]
    (some seek [:first-name :last-name :age])))