1

Say i have a list of elements like [1 2 3] and i wanted to transform it into |1|2|3|. Or if i wanted to repeat the sequence "---" 3 times into "---------".

How should i approach it so that i can build it up into a string like that. Is there a method similar to Java's StringBuilder? Im not looking for a concrete answer to this question but just general guidance as to how to build strings in Clojure as im very new to the language.

3 Answers 3

4

Start with the Clojure CheatSheet. Look at the section "Strings". Some examples:

(str/join \| [1 2 3])           => "1|2|3"

(apply str (repeat 3 "---"))    => "---------"

(str
  "|"
  (str/join \| [1 2 3])
  "|")
       => "|1|2|3|"

There are other libraries that contain many useful string functions in addition to clojure.string:

Sign up to request clarification or add additional context in comments.

Comments

4

also, there is the cl-format function in clojure core library, which is the port of common lisp's amazing format facility.

(require '[clojure.pprint :refer [cl-format]])

user> (cl-format nil "~v@{~a~:*~}" 5 "10")
;;=> "1010101010"

user> (cl-format nil "|~{~a|~}" [1 2 3])
;;=> "|1|2|3|"

this one is really powerful, yet the format string can get quite complicated for the reader, in case of really complex string processing templates. Still, for the cases you ask about (join, repeat, iterate, or conditional output), it stays in the bounds of understandable.

there are some examples here, easily translatable from cl to clojure.

PS:

user> (cl-format nil "~r" 234598284579147)
;;=> "two hundred thirty-four trillion, five hundred ninety-eight billion, two hundred eighty-four million, five hundred seventy-nine thousand, one hundred forty-seven"

user> (cl-format nil "~@r" 1232)
;;=> "MCCXXXII"

Comments

2

The answer to use (apply str ...) is usually the best one. But here is an additional technique, and a "pro tip" about the three dots in (apply str ...).

If the string's content would most naturally be generated by the print functions (which is not the case with your specific examples!), then you can capture it with with-out-str:

(with-out-str
  (doseq [i (range 1 4)]
    (print "|")
    (print i))
  (println "|"))   ;; => "|1|2|3|\n"

Usually, (apply str ...) is more idiomatic. You can use the whole rich tapestry of sequence functions (interleave, interpose, repeat, cycle, ...) and extract the result as a string with (apply str ...). But you face a challenge if the sequence contains nested sequences. We mention this challenge here because there are two solutions that are specific to building up strings.

To be clear, nested sequences "work fine" in every respect except that what str does to a sequence might not be what you want. For example, to build "1------2------3":

;; not quite right:
(apply str 
       (interpose 
         (repeat 2 "---") 
         (range 1 4))) ;; => "1(\"---\" \"---\")2(\"---\" \"---\")3"

The matter is that repeat produced a sequence, which interpose dutifully stuck between the numbers in a bigger sequence, and str when processing the bigger sequence dutifully wrote the nested sequences in Clojure syntax. To better control how nested sequences get stringified, you could replace (repeat 2 "---") with (apply str (repeat 2 "---")). But, if the pattern of apply str within apply str occurs over and over, it hurts the program's signal-to-noise ratio. An alternative that may be cleaner is the flatten function (maybe this is its only idiomatic use):

(apply str 
       (flatten 
        (interpose 
         (repeat 2 "---") 
         (range 1 4))))  ;; => "1------2------3"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.