Simple way to truncate a string in Clojure - clojure

I have a string that must be truncated at 200 characters if it is too long.
Checking the cheatsheet, (subs "Lorem Ipsum" 0 200) would seem to be an obvious choice, but it throws an exception if the second operator is greater than the length of the string.
Is there a simple, built-in function for truncating a string in Clojure? What's the simplest way to do this if I have to define my own?

You can check the length beforehand or use min to determine the actual number of characters that will remain:
(defn trunc
[s n]
(subs s 0 (min (count s) n)))

You can treat them as sequences and get safety (and elegance?) but the cost is performance:
(defn truncate
[s n]
(apply str (take n s)))

Related

How to replace a character in a string using index in clojure

I want to replace a character in a string using index. How to do that? Or is there any other way of accessing it?
Like almost everything commonly used in Clojure, strings are immutable, so you need to create a new string with the new character in place of the old at the desired location:
(defn replace-at [s idx replacement]
(str (subs s 0 idx) replacement (subs s (inc idx))))
> (replace-at "012345" 2 "x")
01x345
Strings are immutable, but StringBuilderss are not, so you could leverage that:
(defn set-char-at [^String s idx ch]
(str (doto (StringBuilder. s) (.setCharAt idx ch))))
(set-char-at "foobar" 2 \x) ;;=> "foxbar"

clojure split string into different size chunks

I'm trying to split a string into n chunks of variable sizes.
As input I have a seq of the sizes of the different chunks:
(10 6 12)
And a string:
"firstchunksecondthirdandlast"
I would like to split the string using the sizes as so:
("firstchunk" "second" "thirdandlast")
As a newbie I still have a hard time wrapping my head around the most idiomatic way to do this.
Here is two ways to do this:
One version uses reduce which you can use very often if you want to carry some kind of state (here: The index where you're currently at). The reduce would need a second fn call applied to it to have the result in your form.
;; Simply take second as a result:
(let [s "firstchunksecondthirdandlast"]
(reduce
(fn [[s xs] len]
[(subs s len)
(conj xs (subs s 0 len))])
[s []]
[10 6 12]))
The other version first builds up the indices of start-end and then uses destructing to get them out of the sequence:
(let [s "firstchunksecondthirdandlast"]
(mapv
(fn [[start end]]
(subs s start end))
;; Build up the start-end indices:
(partition 2 1 (reductions + (cons 0 [10 6 12])))))
Note that neither of these are robust and throw ugly errors if the string it too short. So you should be much more defensive and use some asserts.
Here is my go at the problem (still a beginner with the language), it uses an anonymous function and recursion until the chunks list is empty. I have found this pattern useful when wanting to accumulate results until a condition is met.
str-orig chunks-orig [] sets the initial arguments for the anonymous function: the full string, full list of chunks and an empty vec to collect results into.
(defn split-chunks [str-orig chunks-orig]
((fn [str chunks result]
(if-let [len (first chunks)] (recur
(subs str len)
(rest chunks)
(conj result (subs str 0 len)))
result))
str-orig chunks-orig []))
(split-chunks "firstchunksecondthirdandlast" '(10 6 12))
; ["firstchunk" "second" "thirdandlast"]

Clojure substring with negative index

I want to implement a clojure function that will return the substring in a given string,
Given a string
(def mystring "clojurestring")
(subs mystring 0 3) ;=> "clo"
I want to be able to use a negative index to get sub-string starting from the last character i.e
(subs mystring -13 3) ;=> "clo"
I also think
(subs mystring -9 7) ;=> "ure"
The format for getting the substring in clojure is
(subs 'string' start end)
start. Starting offset, negative offset counts from end of string.Parameter is a positive or negative number.
end. Parameter is an integer greater than zero
so far I have tried without any luck, I don't know if this is possible
Any suggestions? Thanks!
If start is negative, you simply want to subtract from the length of the string. If I understand your requirement correctly:
(defn subs* [s start end]
(if (neg? start)
(subs s (+ (.length s) start) end)
(subs s start end)))
(defn my-subs [st from end]
(let [f (if (neg? from)
(+ (count st) from)
from)]
(subs st f end)))
(my-subs "clojurestring" -9 7)
;"ure"

Printing the binary value of a number in clojure

We can represent the number 12 as 2r001100 in clojure.
Is there a built-in function to print 2r001100 when given the number 12?
java.lang.Integer/toString will print numbers with arbitrary radix:
(Integer/toString 0xf2 2) ==> "11110010"
(Integer/toString 0xf2 16) ==> "f2"
(Integer/toString 0xf2 27) ==> "8q"
see cl-format
user=> (require '[clojure.pprint :refer (cl-format)])
nil
user=> (cl-format nil "2r~6,'0',B" 12)
"2r001100"
These functions generate and print strings using java.util.Formatter.
format
printf
But they don't do binary, so the best I could come up with is:
(fn [i] (str "2r" (Integer/toBinaryString i)))
All of these answers are good, but either won't support two's-complement for negative numbers (cl-format), or won't print out the correct number of bits based on the width of the data itself (e.g., calling Integer/toBinaryString or Integer/toString on a byte will not do what you want, especially for negative numbers).
Here's a solution that will correctly print out the exact bits of the underlying data:
(defn print-bits [b]
(let [class-name (.getName (class b))
is-byte (= "java.lang.Byte" class-name)
num-bits (clojure.lang.Reflector/getStaticField class-name "SIZE")
format-string (str "~" num-bits "'0b")
bin-str-fn #(clojure.lang.Reflector/invokeStaticMethod
(if is-byte "java.lang.Integer" class-name)
"toBinaryString"
(to-array [%]))
bit-string (if is-byte
(str/join (take-last 8 (bin-str-fn (Byte/toUnsignedInt b))))
(bin-str-fn b))]
(println (str (str/join (repeat (- num-bits (count bit-string)) \0))
bit-string))))
Test of extremes here, using (bit-shift-left 1 63), or 1000000000000000000000000000000000000000000000000000000000000000.
The cl-format solution provided gives me an integer overflow.
Integer/toBinaryString gives me Value out of range for int: -9223372036854775808.
But Long/toBinaryString gives me the string that I expected.

Clojure: generate all keyboard typeable characters

Context
I want to generate all characters that can be generated by:
opening note pad
pressing a single key on the keyboard
holding shift + pressing a single key on the keyboard
What I currently have:
(concat (range (int \a) (int \z))
(range (int \A) (int \Z))
(range (int \0) (int \9)))
then manually appending more characters like ~!##$%^&*()_+{}|:"<>?,./;'[]\
Question
Is there a more elegant way of doing this?
Edits
Yes, I'm referring to US Qwerty keyboard.
If you look at a US ASCII chart, it seems that all the characters you want are within (range 33 127). So the simplest way to get a sequence of all those characters is to convert that range to characters.
(map char (range 33 127))
But if you are trying to validate that a string contains only those characters, have a function like:
(defn valid-char? [c]
(let [i (int c)]
(and (> i 32) (< i 127))))
Then you can use it with every? to validate a string:
user=> (every? valid-char? "hello world")
true
user=> (every? valid-char? "héllo world")
false
Using the following map form will generate the characters you want.
(map #(str (char %)) (range 32 127))