How can I convert a LazySeq of Characters to a String in Clojure?
Let's say I have a LazySeq of java.lang.Character like
(\b \ \! \/ \b \ \% \1 \9 \/ \. \i \% \$ \i \space \^@)
How can I convert this to a String? I've tried the obvious
but it throws
java.lang.IllegalArgumentException: No matching ctor found for class java.lang.String (NO_SOURCE_FILE:0) [Thrown class clojure.lang.Compiler$CompilerException]
I think because the String constructor expects a primitive char instead of a LazySeq. So then I tried something like
(String. (into-array my-char-seq))
but it throws the same exception. The problem now is that into-array is returning a java.lang.Character instead of a primitive char. This is frustrating, because I actually generate my character sequence like this
(map #(char (Integer. %)) seq-of-ascii-ints)
Basically I have a seq of ints representing ASCII characters; 65 = A, etc. You can see I explicitly use the primitive type coercion function (char x).
What this means is that my map function is returning a primitive char but the Clojure map function overall is returning the java.lang.Character object.
(apply str my-char-seq)
Basically, str calls toString() on each of its args and then concatenates them. Here we are using apply to pass the characters in the sequence as args to str.
Another way is to use clojure.string/join, as follows:
(require '[clojure.string :as str] ) (assert (= (vec "abcd") [\a \b \c \d] )) (assert (= (str/join (vec "abcd")) "abcd" )) (assert (= (apply str (vec "abcd")) "abcd" ))
There is an alternate form of clojure.string/join which accepts a separator. See:
For more complicated problems, you may also wish to lookat strcat from the Tupelo library:
(require '[tupelo.core :as t] ) (prn (t/strcat "I " [ \h \a nil \v [\e \space (byte-array ) [ nil 32 "complicated" (Math/pow 2 5) '( "str" nil "ing") ]]] )) ;=> "I have a complicated string"
As a special case, if the underlying type of the sequence in question is clojure.lang.StringSeq you can also do:
which is extremely performant as it is just pulling out the public final CharSequence field from the clojure StringSeq class.
(type (seq "foo")) => clojure.lang.StringSeq (.s (seq "foo")) => "foo" (type (.s (seq "foo"))) => java.lang.String
an example of the timing implications (and note the difference when using a type hint):
(time (let [q (seq "xxxxxxxxxxxxxxxxxxxx")] (dotimes [_ 1000000] (apply str q)))) "Elapsed time: 620.943971 msecs" => nil (time (let [q (seq "xxxxxxxxxxxxxxxxxxxx")] (dotimes [_ 1000000] (.s q)))) "Elapsed time: 1232.119319 msecs" => nil (time (let [^StringSeq q (seq "xxxxxxxxxxxxxxxxxxxx")] (dotimes [_ 1000000] (.s q)))) "Elapsed time: 3.339613 msecs" => nil