Search code examples
clojure

idiomatic clojure prefix replacement


I have a map representing information about a subversion commit.

Example contents:

(def commit 
  {:repository "/var/group1/project1"
   :revision-number "1234"
   :author "toolkit"
   etc..}

I would like to change the repository based on a prefix match, so that:

/var/group1 maps to http://foo/group1
/var/group2 maps to http://bar/group2

I have created 2 functions like:

(defn replace-fn [prefix replacement]
  (fn [str]
    (if (.startsWith str prefix)
      (.replaceFirst str prefix replacement)
      str)))

(def replace-group1 (replace-fn "/var/group1" "http://foo/group1"))
(def replace-group2 (replace-fn "/var/group2" "http://bar/group2"))

And now I have to apply them:

(defn fix-repository [{:keys [repository] :as commit}]
  (assoc commit :repository
    (replace-group1
      (replace-group2 repository))))

But this means I have to add an extra wrapper in my fix-repository for each new replacement.

I would like to simply:

  • Given a commit map
  • Extract the :repository value
  • Loop through a list of replacement prefixes
  • If any prefix matches, replace :repository value with the new string
  • Otherwise, leave the :repository value alone.

I can't seem to build the right loop, reduce, or other solution to this.


Solution

  • How about something like this?

    (defn replace-any-prefix [replacements-list string]
      (or (first
            (filter identity
              (map (fn [[p r]]
                     (when (.startsWith string p)
                        (.replaceFirst string p r)))
                   replacements-list)))
          string)))
    
    (update-in commit
               [:repository]
               (partial replace-any-prefix
                        [["/var/group1" "http://foo/group1"]
                         ["/var/group2" "http:/foo/group2"]]))
    

    Documentation for update-in: http://clojuredocs.org/clojure_core/clojure.core/update-in