Reputation: 1558
First of all, I am not sure how to easily word the title.
The problem I have is give a string insert value here ?
I want to be able to swap the ?
with a value of my choice, i can do this using clojure.string/replace
.
Now, the use case I require is slightly more complex, given a string like:
these are the specified values: ?, ?, ?, ?
I want to replace the values of the ?
with values from a collection which could look like:
[2 389 90 13]
so in this example the string would now read:
these are the specified values: 2, 389, 90, 13
so ? x
maps to collection x
(e.g. ? 0
maps to collection 0
)
The number of ?
will not always be 4 or a specific n, however the length of the collection will always be the same as the number of ?
.
I tried doing the following:
(mapv #(clojure.string/replace-first statement "?" %) [1 2 3 4])
But this doesn't produce the desired results in produces a vector
of size 4 where only the first ?
is replaced by the value.
I am lost due the inability to modify variables in clojure, and I don't want to have a global string which is redefined and passed to a function n
times.
Upvotes: 3
Views: 601
Reputation: 3014
While I agree that DaoWen's answer is likely the most practical, the end of your question seems worth discussing a little as well as a matter of learning functional approaches. You're essentially looking for a way to
replace-first
to make another string from them.replace-first
on them.This is actually a classic pattern in mathematics and functional programming, called a "left fold" or "reduction" over the value sequence. Functional languages usually build it into their standard libraries as a higher order function. In Clojure it's called reduce
. Implementing your attempted strategy with it looks something like
(reduce #(clojure.string/replace-first %1 "?" %2)
"these are the specified values: ?, ?, ?, ?"
[2 389 90 13])
; => "these are the specified values: 2, 389, 90, 13"
Note that unlike your similar function literal, this takes two arguments, so that the statement
can be rebound as we proceed through the reduction.
If you want to see what happens along the way with a reduce
, you can swap it out with reductions
. Here you get
("these are the specified values: ?, ?, ?, ?" ;After 0 applications of our replace-first fn
"these are the specified values: 2, ?, ?, ?" ;;Intermediate value after 1 application
"these are the specified values: 2, 389, ?, ?" ;;after 2...
"these are the specified values: 2, 389, 90, ?"
"these are the specified values: 2, 389, 90, 13");;Final value returned by reduce
Upvotes: 6
Reputation: 10624
DaoWen has given the pragmatic answer for strings but you do have to replace your "?" with "%s" first.
But suppose this wasn't a string. Clojure has a great collection library that often means you can avoid recursion or reducers but I couldn't think of a way to do that here without getting really inefficient and messy. So here's a reduce
solution applicable to non-strings, with lots of comments.
(let [original "these are the specified values: ?, ?, ?, ?"
replacements [2 389 90 13]
starting-accumulator [[] replacements] ; Start with an empty vector and all of the replacements.
reducing-fn (fn [[acc [r & rs :as all-r]] o] ; Apply destructuring liberally
(if (= o \?) ; If original character is "?".
[(conj acc r) rs] ; Then add the replacement character and return the rest of the remaining replacements.
[(conj acc o) all-r])) ; Else add the original character and return all the remaining replacements.
reduced (reduce reducing-fn starting-accumulator original) ; Run the reduce.
result (first reduced) ; Get the resulting seq (discard the second item: the remaining empty seq of replacements).
string-joined (apply str result)] ; The string was turned into a seq of chars by `reduce`. Turn it back into a string.
string-joined)
Upvotes: 1
Reputation: 33019
There might be other considerations that aren't covered by your question—but as written, it seems like you should just be using the string format
function:
(apply format
"these are the specified values: %s, %s, %s, %s"
[2 389 90 13])
; => "these are the specified values: 2, 389, 90, 13"
Upvotes: 5