string string manipulation

Author: Chris Zheng  (z@caudate.me)
Date: 27 November 2018
Repository: https://github.com/zcaudate/hara
Version: 3.0.2

1    Introduction

hara.string contains methods for string manipulation.

1.1    Installation

Add to project.clj dependencies:

[hara/base "3.0.2"]

All functions are in the hara.string namespace.

 (use (quote hara.string))

2    General



^

compares two string-like things

v 3.0
(defn =
  [x y]
  (clojure.core/= (impl/to-string x)
                  (impl/to-string y)))
link
(string/= :a 'a) => true (string/= *ns* :hara.string-test) => true

blank? ^

checks if string is empty or nil

v 3.0
(defn blank?
  [^CharSequence s]
  (if s
    (loop [index (int 0)]
      (if (= (.length s) index)
        true
        (if (Character/isWhitespace (.charAt s index))
          (recur (inc index))
          false)))
    true))
link
(blank? nil) => true (blank? "") => true

capital-case ^

converts a string object to capital case

v 3.0
(defn ^String capital-case
  [^CharSequence s]
  (let [s (.toString s)]
    (if (< (count s) 2)
      (.toUpperCase s)
      (str (.toUpperCase (subs s 0 1))
           (.toLowerCase (subs s 1))))))
link
(capital-case "hello.World") => "Hello.world" (string/capital-case 'hello.World) => 'Hello.world

caseless= ^

compares two values ignoring case

v 3.0
(defn caseless=
  [x y]
  (= (lower-case x)
     (lower-case y)))
link
(caseless= "heLLo" "HellO") => true (string/caseless= 'heLLo :HellO) => true

copy-string-var ^

creates a function, augmenting it with string conversion properties

v 3.0
(defn copy-string-var
  ([type return ns name ^clojure.lang.Var source]
   (let [func (case type
                :op  (impl/wrap-op @source return)
                :compare (impl/wrap-compare @source))
         sink (intern ns name func)]
     (alter-meta! sink
                  merge
                  (-> (meta source)
                      (dissoc :name :ns)))
     sink)))
link
(string/copy-string-var :op false *ns* '-subs- #'string/subs) => #'hara.string-test/-subs- (-subs- :hello 3) => :lo (-subs- :hello 1 4) => :ell

ends-with? ^

checks if string ends with another

v 3.0
(defn ends-with?
  [^CharSequence s ^String substr]
  (.endsWith (.toString s) substr))
link
(ends-with? "hello" "lo") => true (string/ends-with? 'hello 'lo) => true

from-string ^

meta information of keywords and symbols

v 3.0
link
(string/from-string "hello/world" clojure.lang.Symbol) => 'hello/world (string/from-string "hello/world" clojure.lang.Keyword) => :hello/world

includes? ^

checks if first string contains the second

v 3.0
(defn includes?
  [^CharSequence s ^CharSequence substr]
  (.contains (.toString s) substr))
link
(includes? "hello" "ell") => true (string/includes? 'hello 'ell) => true

join ^

joins a list together

v 3.0
(defn join
  ([arr]
   (join arr common/*sep*))
  ([sep arr]
   (impl/from-string (common/join sep (map impl/to-string arr))
                     (type (first arr)))))
link
(string/join "." [:a :b :c]) => :a.b.c

joinl ^

joins an array using a separator

v 3.0
(defn ^String joinl
  ([coll]
   (apply str coll))
  ([coll separator]
     (loop [sb (StringBuilder. (str (first coll)))
            more (next coll)
            sep (str separator)]
       (if more
         (recur (-> sb (.append sep) (.append (str (first more))))
                (next more)
                sep)
         (str sb)))))
link
(joinl ["a" "b" "c"] ".") => "a.b.c" (joinl ["a" "b" "c"]) => "abc" (string/joinl [:a :b :c] "-") => :a-b-c

lower-case ^

converts a string object to lower case

v 3.0
(defn ^String lower-case
  [^CharSequence s]
  (.. s toString toLowerCase))
link
(lower-case "Hello.World") => "hello.world" (string/lower-case 'Hello.World) => 'hello.world

path-separator ^

returns the default path separator for an object

v 3.0
(definvoke path-separator
  [:memoize {:arglists '([type])
             :function protocol.string/-path-separator}])
link
(path-separator clojure.lang.Namespace) => "." (path-separator clojure.lang.Keyword) => "/"

replace ^

replace value in string with another

v 3.0
(defn ^String replace
  [^CharSequence s match replacement]
  (let [s (.toString s)]
    (cond 
     (instance? Character match) (.replace s ^Character match ^Character replacement)
     (instance? CharSequence match) (.replace s ^CharSequence match ^CharSequence replacement)
     (instance? Pattern match) (if (instance? CharSequence replacement)
                                 (.replaceAll (re-matcher ^Pattern match s)
                                              (.toString ^CharSequence replacement))
                                 (replace-by s match replacement))
     :else (throw (IllegalArgumentException. (str "Invalid match arg: " match))))))
link
(replace "hello" "el" "AL") => "hALlo" (string/replace :hello "el" "AL") => :hALlo

reverse ^

reverses the string

v 3.0
(defn ^String reverse
  [^CharSequence s]
  (.toString (.reverse (StringBuilder. s))))
link
(reverse "hello") => "olleh" (string/reverse :hello) => :olleh

split ^

splits a string given a regex

v 3.0
link
(string/split "a b" #" ") => ["a" "b"] (string/split " " #" ") => ["" ""]

split-lines ^

splits a string given newlines

v 3.0
link
(string/split-lines "anb") => ["a" "b"] (string/split-lines "n") => ["" ""]

starts-with? ^

checks if string starts with another

v 3.0
(defn starts-with?
  [^CharSequence s ^String substr]
  (.startsWith (.toString s) substr))
link
(starts-with? "hello" "hel") => true (string/starts-with? 'hello 'hel) => true

subs ^

compares two string-like things

v 3.0
link
(string/subs :hello-world 3 8) => :lo-wo (string/format :hello%d-world 100) => :hello100-world

to-string ^

converts symbols and keywords to string representation

v 3.0
link
(string/to-string 'hello/world) => "hello/world" (string/to-string :hello/world) => "hello/world"

trim ^

trims the string of whitespace

v 3.0
(defn ^String trim
  [^CharSequence s]
  (let [len (.length s)]
    (loop [rindex len]
      (if (zero? rindex)
        ""
        (if (Character/isWhitespace (.charAt s (dec rindex)))
          (recur (dec rindex))
          ;; there is at least one non-whitespace char in the string,
          ;; so no need to check for lindex reaching len.
          (loop [lindex 0]
            (if (Character/isWhitespace (.charAt s lindex))
              (recur (inc lindex))
              (.. s (subSequence lindex rindex) toString))))))))
link
(trim " hello ") => "hello"

trim-left ^

trims the string of whitespace on left

v 3.0
(defn ^String trim-left
  [^CharSequence s]
  (let [len (.length s)]
    (loop [index 0]
      (if (= len index)
        ""
        (if (Character/isWhitespace (.charAt s index))
          (recur (unchecked-inc index))
          (.. s (subSequence index len) toString))))))
link
(trim-left " hello ") => "hello "

trim-newlines ^

removes newlines from right

v 3.0
(defn ^String trim-newlines
  [^CharSequence s]
  (loop [index (.length s)]
    (if (zero? index)
      ""
      (let [ch (.charAt s (dec index))]
        (if (or (= ch newline) (= ch return))
          (recur (dec index))
          (.. s (subSequence 0 index) toString))))))
link
(trim-newlines "nn hello nn") => "nn hello "

trim-right ^

trims the string of whitespace on right

v 3.0
(defn ^String trim-right
  [^CharSequence s]
  (loop [index (.length s)]
    (if (zero? index)
      ""
      (if (Character/isWhitespace (.charAt s (unchecked-dec index)))
        (recur (unchecked-dec index))
        (.. s (subSequence 0 index) toString)))))
link
(trim-right " hello ") => "hello"

upper-case ^

converts a string object to upper case

v 3.0
(defn ^String upper-case
  [^CharSequence s]
  (.. s toString toUpperCase))
link
(upper-case "hello-world") => "HELLO-WORLD" (string/upper-case :hello-world) => :HELLO-WORLD

3    Type



camel-case ^

converts a string-like object to camel case representation

v 3.0
(defn camel-case
  [^String value]
  (re-sub value
          +non-camel-pattern+
          (fn [s] (common/upper-case (apply str (rest s))))))
link
(camel-case "hello-world") => "helloWorld" (string/camel-case 'hello_world) => 'helloWorld

capital-sep-case ^

converts a string-like object to captital case representation

v 3.0
(defn capital-sep-case
  [^String value]
  (-> (separate-humps value)
      (common/split #"[ |-|_]")
      (->> (map common/capital-case))
      (common/joinl " ")))
link
(capital-sep-case "hello world") => "Hello World" (str (string/capital-sep-case :hello-world)) => ":Hello World"

lower-sep-case ^

converts a string-like object to a lower case representation

v 3.0
(defn lower-sep-case
  [^String value]
  (-> (separate-humps value)
      (common/split #"[ |-|_]")
      (->> (map common/lower-case))
      (common/joinl " ")))
link
(lower-sep-case "helloWorld") => "hello world" (string/lower-sep-case 'hello-world) => (symbol "hello world")

pascal-case ^

converts a string-like object to a pascal case representation

v 3.0
(defn pascal-case
  [^String value]
  (let [s (camel-case value)]
    (str (.toUpperCase (subs s 0 1))
         (subs s 1))))
link
(pascal-case "helloWorld") => "HelloWorld" (string/pascal-case :hello-world) => :HelloWorld

phrase-case ^

converts a string-like object to snake case representation

v 3.0
(defn phrase-case
  [^String value]
  (let [s (lower-sep-case value)]
    (str (.toUpperCase (subs s 0 1))
         (subs s 1))))
link
(phrase-case "hello-world") => "Hello world"

snake-case ^

converts a string-like object to snake case representation

v 3.0
(defn snake-case
  [value]
  (-> (separate-humps value)
      (common/lower-case)
      (common/replace +non-snake-pattern+ "_")))
link
(snake-case "hello-world") => "hello_world" (string/snake-case 'helloWorld) => 'hello_world

spear-case ^

converts a string-like object to spear case representation

v 3.0
(defn spear-case
  [value]
  (-> (separate-humps value)
      (common/lower-case)
      (common/replace +non-spear-pattern+ "-")))
link
(spear-case "hello_world") => "hello-world" (string/spear-case 'helloWorld) => 'hello-world

typeless= ^

compares two representations

v 3.0
(defn typeless=
  [x y]
  (= (lower-sep-case x)
     (lower-sep-case y)))
link
(typeless= "helloWorld" "hello_world") => true (string/typeless= :a-b-c "a b c") => true (string/typeless= 'getMethod :get-method) => true

upper-sep-case ^

converts a string-like object to upper case representation

v 3.0
(defn upper-sep-case
  [^String value]
  (-> (separate-humps value)
      (common/split #"[ |-|_]")
      (->> (map common/upper-case))
      (common/joinl " ")))
link
(upper-sep-case "hello world") => "HELLO WORLD" (str (string/upper-sep-case 'hello-world)) => "HELLO WORLD"

4    Path



path-count ^

counts the number of elements in a given path

v 3.0
(defn path-count
  ([s]
   (path-count s common/*sep*))
  ([s sep]
   (count (path-split s sep))))
link
(path/path-count "a/b/c") => 3 (string/path-count *ns*) => 4

path-join ^

joins a sequence of elements into a path separated value

v 3.0
(defn path-join
  ([arr] (path-join arr common/*sep*))
  ([arr sep]
   (if (seq arr)
     (-> (filter identity arr)
         (common/joinl sep)))))
link
(path/path-join ["a" "b" "c"]) => "a/b/c" (string/path-join '[:a :b :c] "-") => :a-b-c (string/path-join '[a b c] '-) => 'a-b-c

path-ns ^

returns the path namespace of the string

v 3.0
(defn path-ns
  ([s]
   (path-ns s common/*sep*))
  ([s sep]
   (-> s
       (path-ns-array sep)
       (path-join sep))))
link
(path/path-ns "a/b/c/d") => "a/b/c" (string/path-ns :a.b.c ".") => :a.b

path-ns-array ^

returns the path vector of the string

v 3.0
(defn path-ns-array
  ([s]
   (path-ns-array s common/*sep*))
  ([s sep]
   (or (butlast (path-split s sep)) [])))
link
(path/path-ns-array "a/b/c/d") => ["a" "b" "c"] (string/path-ns-array (keyword "a/b/c/d")) => [:a :b :c]

path-nth ^

check for the val of the string

v 3.0
(defn path-nth
  ([s n]
   (path-nth s n common/*sep*))
  ([s n sep]
   (nth (path-split s sep) n)))
link
(path/path-nth "a/b/c/d" 2) => "c"

path-root ^

returns the path root of the string

v 3.0
(defn path-root
  ([s]
   (path-root s common/*sep*))
  ([s sep]
   (first (path-ns-array s sep))))
link
(path/path-root "a/b/c/d") => "a" (string/path-root 'a.b.c ".") => 'a

path-split ^

splits a sequence of elements into a path separated value

v 3.0
(defn path-split
  ([s] (path-split s common/*sep*))
  ([s sep]
   (common/split s (make-pattern sep))))
link
(path/path-split "a/b/c/d") => '["a" "b" "c" "d"] (path/path-split "a.b.c.d" ".") => ["a" "b" "c" "d"] (string/path-split :hello/world) => [:hello :world] (string/path-split :hello.world ".") => [:hello :world]

path-stem ^

returns the path stem of the string

v 3.0
(defn path-stem
  ([s]
   (path-stem s common/*sep*))
  ([s sep]
   (-> s
       (path-stem-array sep)
       (path-join sep))))
link
(path/path-stem "a/b/c/d") => "b/c/d" (string/path-stem 'a.b.c.d ".") => 'b.c.d

path-stem-array ^

returns the path stem vector of the string

v 3.0
(defn path-stem-array
  ([s]
   (path-stem-array s common/*sep*))
  ([s sep]
   (rest (path-split s sep))))
link
(path/path-stem-array "a/b/c/d") => ["b" "c" "d"] (string/path-stem-array 'a.b.c.d ".") => '[b c d]

path-sub ^

returns a subsection of the path within the string

v 3.0
(defn path-sub
  ([s start num]
   (path-sub s start num common/*sep*))
  ([s start num sep]
   (-> (path-sub-array s start num sep)
       (path-join sep))))
link
(path/path-sub "a/b/c/d" 1 2) => "b/c" (string/path-sub (symbol "a/b/c/d") 1 2) => 'b/c

path-sub-array ^

returns a sub array of the path within the string

v 3.0
(defn path-sub-array
  ([s start num]
   (path-sub-array s start num common/*sep*))
  ([s start num sep]
   (->> (path-split s sep)
        (drop start)
        (take num))))
link
(path/path-sub-array "a/b/c/d" 1 2) => ["b" "c"] (string/path-sub-array (symbol "a/b/c/d") 1 2) => '[b c]

path-val ^

returns the val of the string

v 3.0
(defn path-val
  ([s]
   (path-val s common/*sep*))
  ([s sep] 
   (last (path-split s sep))))
link
(path/path-val "a/b/c/d") => "d" (string/path-val 'a.b.c.d ".") => 'd