In clojure, how to write a function that applies multiple string replacements?

I would like to write a replace-several function that receives a string and a set of substitutions and applies all the replacements (where the replacements see the result of previous notes).

I thought of the following interface:

 (replace-several "abc" #"a" "c" #"b" "l" #"c" "j"); should return "jlj" 

Two questions:

  • Is this the most idiomatic interface in clojure?
  • How to implement this function?

Note. To make a replacement, there is replace available in clojure.string .

+8
clojure
source share
7 answers

Implement @kotarak recommendations with replace , reduce and partition :

 (defn replace-several [content & replacements] (let [replacement-list (partition 2 replacements)] (reduce #(apply string/replace %1 %2) content replacement-list))) ; => (replace-several "abc" #"a" "c" #"b" "l" #"c" "j") "jlj" 
+11
source share

So you have replace , reduce and partition . From these building blocks, you can create your replace-several .

+8
source share

Here is another shot, but with different output results , this uses the functions of the regex mechanism, so it can be faster, and the interface is different because it matches keys with replacement strings. I provide this if it can be helpful to someone with a similar question.

 (defn replace-map "given an input string and a hash-map, returns a new string with all keys in map found in input replaced with the value of the key" [sm] (clojure.string/replace s (re-pattern (apply str (interpose "|" (map #(java.util.regex.Pattern/quote %) (keys m))))) m)) 

So the usage will be like this:

  (replace-map "abc" {"a" "c" "b" "l" "c" "j"}) 

=> "clj"

+4
source share

I'm late for this party, but for what it's worth, I think the most idiomatic way to do this would be to use threads and multiple replacements:

 (require '[clojure.string :refer [replace]) (-> "abc" (replace #"a" "c") (replace #"b" "l") (replace #"c" "j")) ;=> "jlj" 

The meaning of this is clear enough, although it is useful to avoid repeatedly entering "replace".

+2
source share

You can use abbreviation with replacement:

 (defn replace-several [str & replacements] (reduce (fn [s [ab]] (clojure.string/replace sab)) str (partition 2 replacements))) (replace-several "abc" #"a" "c" #"b" "l" #"c" "j") 
+1
source share

guess first.

 (defn replace-several [string & mappings] (loop [grouped-mappings (partition 2 mappings) string string] (if (empty? grouped-mappings) string (let [[re rep] (first grouped-mappings)] (recur (rest grouped-mappings) (clojure.string/replace string re rep)))))) ; => (replace-several "abc" #"a" "c" #"b" "l" #"c" "j") "jlj" 
0
source share

Personally, I would not create a separate function for this, since this is just a composition of existing Clojure functions:

(reduce-kv clojure.string/replace "Hello" {#"[AZ]" "J", "o" "y"} ;=> "Jelly"

Of course, if you want varargs and an interface, then:

  • Yes it seems reasonable idiomatic
  • I would do it like this:

(defn replace-many [string & {:as rplcmnts}] (reduce-kv clojure.string/replace string rplcmnts))

0
source share

All Articles