Search

Dark theme | Light theme

March 29, 2020

Clojure Goodness: Replacing Matching Values In String

We can search for a value in a string and replace it with another value using the clojure.string/replace function. The first parameter is the original string value that we want to replace parts of. The second parameter can be a string value or regular expression. The last parameter is the replacement value that can be a string value or a function that returns a string value. The function itself gets either a string argument if the match has no nested groups (when match is a regular expression) or a vector with a complete match followed by the nested groups when the match has nested groups.

In the following example we several invocation of the clojure.string/replace function with different arguments:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
(ns mrhaki.string.replace
  (:require [clojure.string :as str]
            [clojure.test :refer [is]]))
 
;; Example string value to do replacements on.
(def s "Programming with Clojure is fun!")
 
;; Match argument can be a string value,
;; that gives same result as java.lang.String#replace method.
(is (= "Programming with Clojure is awesome!"
       (str/replace s "fun" "awesome")
       (.replace s "fun" "awesome")))
 
 
;; Match argument can also be regular expression pattern.
(is (= "Programming_with_Clojure_is_fun!"
       (str/replace s #"\s+" "_")))
 
;; When the regular expression pattern has groups
;; we can refer to them using $ followed by matched
;; group number, eg. $1 for the first group.
(is (= "Execution 1 took 200ms"
       (str/replace "run1=200ms" #"run(\d+)=(\d+ms)" "Execution $1 took $2")))
 
 
;; Replace argument can be a function.
;; Argument of the function is string of entire match
;; if there are no nested groups.
(is (= "[NOTE] [CAUTION]"
       (str/replace "[note] [caution]" #"\[\w+\]" #(.toUpperCase %))))
 
;; Otherwise if there are nested groups a vector is
;; used as argument for the replacment function
;; where the first argument is the
;; entire match followed by the nested groups.
(is (= "ABC def"
       (str/replace "abc DEF"
                    #"(\w+)(\s+)(\w+)"
                    #(str (.toUpperCase (% 1)) (% 2) (.toLowerCase (% 3))))))
 
;; By destructuring the vector argument
;; we can refer to the groups using a name.
(defn replacement
  [[_ execution time]]
  (let [seconds (/ (bigdec time) 1000)]
    (str "Execution " execution " took " seconds " seconds")))
 
(is (= "Execution 1 took 0.2 seconds"
       (str/replace "run1=200ms" #"run(\d+)=(\d+)ms" replacement)))

Written with Clojure 1.10.1.