hithacker
hithacker

Reputation: 151

Is there a built-in function in Clojure to replace a character at a specific index in a string?

e.g. I want to replace a character at index 2 in string "123456789" with character 'X'. I know that I can do something like (str (subs "123456789" 0 2) "X" (subs "123456789" 3)) but I want to know if there is already a built-in library function for this.

Upvotes: 4

Views: 858

Answers (3)

leetwinski
leetwinski

Reputation: 17859

another way, is to use java's StringBuilder, which has this exact api, and should be probably faster, than subs or vec (and obviously faster in case you do a number of changes at once):

user> (let [sb (StringBuilder. "123456789")]
        (str (.replace sb 1 2 "x")))
"1x3456789"

multiple changes:

user> (let [sb (StringBuilder. "123456789")]
        (doseq [idx [1 3 5]]
          (.replace sb idx (inc idx) "x"))
        (str sb))
"1x3x5x789"

a little performance benchmark:

user> (defn replace-builder [^String s]
        (let [sb (StringBuilder. s)]
          (.replace sb 2 3 "x")
          (str sb)))
#'user/replace-builder

user> (defn replace-vec [^String s]
        (clojure.string/join (assoc (vec s) 2 \x)))
#'user/replace-vec

user> (defn replace-subs [^String s]
        (str (subs s 0 2) "x" (subs s 3)))
#'user/replace-subs

user> (time (dotimes [_ 1000000] (replace-builder "123456789")))
"Elapsed time: 78.457028 msecs"
nil

user> (time (dotimes [_ 1000000] (replace-vec "123456789")))
"Elapsed time: 917.656951 msecs"
nil

user> (time (dotimes [_ 1000000] (replace-subs "123456789")))
"Elapsed time: 224.585971 msecs"
nil

Upvotes: 6

Mars
Mars

Reputation: 8854

Piotr is right, but here's another way to do it. It's not really shorter, but I think it's slightly easier to understand.

(clojure.string/join (assoc (vec "abc") 1 \B))  ;=> "aBc"
(clojure.string/join (assoc (vec "abc") 1 "B")) ;=> "aBc"

You can also use apply str or reduce str instead of join.

Clojure strings are treated like vectors when passed to get:

(get "abc" 1) ;=> \b

However, they don't seem to be treated as vectors in any other context. This won't work:

(assoc "abc" 1 \B)
ClassCastException java.lang.String cannot be cast to clojure.lang.Associative ...

(For the record, clojure.string/replace and clojure.string/replace-first will search for a matching substring and replace it, but that's not what you asked about.)

Upvotes: 2

Piotrek Bzdyl
Piotrek Bzdyl

Reputation: 13175

Unfortunately, there is no such function in Clojure API. Even java.lang.String doesn't have such method. You need to implement it by yourself.

Upvotes: 3

Related Questions