Trimming
Basic string trimming is handled by the API function trim-both
from the namespace smangler.api
. In its single-argument version it trims a given string on both ends if the characters are the same. It repeats this operation until there is nothing to trim.
(require '[smangler.api :as sa])
(sa/trim-both "abba") ; => ""
(sa/trim-both "some") ; => "some"
(sa/trim-both "barkrab") ; => "k"
Coercion to strings
You can pass other types of arguments and they will be coerced to strings. Single characters and numbers are supported, and so are collections of strings, characters and numbers:
(require '[smangler.api :as sa])
(sa/trim-both 1111) ; => ""
(sa/trim-both 12345) ; => "12345"
(sa/trim-both 12021) ; => "0"
(sa/trim-both \a) ; => "a"
(sa/trim-both [1 0 1]) ; => "0"
(sa/trim-both [\a \b \a]) ; => "b"
(sa/trim-both ["abc" "ba"]) ; => "c"
Custom matcher
Optionally, you can call trim-both
with 2 arguments passed. In this scenario the first argument should be a function which takes a single character and returns either some character, nil
or false
. This function, called the matcher, is used on the first character of a string to determine whether it should be removed (along with the last character):
(fn [character]
(and (some-lookup character) ; some-lookup checks if a character should be trimmed
character)) ; important to return the character, nil or false
If the matcher returns nil
or false
then no trimming occurs. If the matcher returns a character it is compared by trim-both
with the right-most character of the trimmed string and if they’re equal then trimming is performed.
(require '[smangler.api :as sa])
;; Only 'a' will be trimmed since the matcher
;; checks if it is this letter.
(sa/trim-both #(and (= \a %) %) "abxba") ; => "bxb"
;; Beginning 'a' and ending 'z' will be trimmed
;; because matcher requires that 'a' must have corresponding 'z'.
(sa/trim-both #(and (= \a %) \z) "abcdz") ; => "bcd"
Sets as matchers
It is common to use sets for matching the same characters on both ends. This is possible because in Clojure sets implement function interface which allows us to perform quick lookup:
(require '[smangler.api :as sa])
(sa/trim-both #{\a \b} "abxba") ; => "x"
(sa/trim-both #{\a} "abxba") ; => "bxb"
Maps as matchers
Due to the characteristics of a matching function it is possible to use maps as since they also implement function interface. That will allow us to match both ends of a string in an easy way:
(require '[smangler.api :as sa])
(sa/trim-both {\a \a} "abxba") ; => "bxb"
(sa/trim-both {\a \z} "abcdz") ; => "bcd"
Coercion to matcher
You can pass other types of arguments and they will be coerced to matchers. Single characters, strings and numbers are supported, and so are collections of strings, characters and numbers:
(require '[smangler.api :as sa])
(sa/trim-both \a "abxba") ; => "bxb"
(sa/trim-both 1 "1abc1") ; => "abc"
(sa/trim-both 12 "21abc12") ; => "abc"
(sa/trim-both [1 2] "21abc12") ; => "abc"
(sa/trim-both [\a \b] "abxba") ; => "x"
(sa/trim-both "ab" "abxba") ; => "x"
Two characters and a string
In its 3-argument version the function simply takes 2 characters and a string. The given characters must match first and last characters of the string for trimming to be performed:
(require '[smangler.api :as sa])
(sa/trim-both \a \a "abxba") ; => "bxb"
(sa/trim-both \a \a "aaxaa") ; => "x"
(sa/trim-both \1 \1 "1abc1") ; => "abc"
(sa/trim-both \a \z "axz") ; => "x"
(sa/trim-both \a \z "aaxzz") ; => "x"
Trimming once
If you want to perform trimming just once, use trim-both-once
API function. It works the same way as trim-both
but stops after first operation (if any):
(require '[smangler.api :as sa])
(sa/trim-both-once "abba") ; => "bb"
(sa/trim-both-once "some") ; => "some"
(sa/trim-both-once "barkrab") ; => "arkra"
(sa/trim-both-once "ab" "barkrab") ; => "arkra"
(sa/trim-both-once #{\a \b} "barkrab") ; => "arkra"
(sa/trim-both-once \a \a "aaxaa") ; => "axa"
Trimming once with preservation
To make certain operations easy there is trim-both-once-with-orig
function which returns a sequence of 1 or 2 elements, the first being always the original string with the latter being its trimmed version. If there is nothing to trim, only one element will be present in the resulting sequence:
(require '[smangler.api :as sa])
(sa/trim-both-once-with-orig "abba") ; => ("abba", "bb")
(sa/trim-both-once-with-orig "some") ; => ("some")
(sa/trim-both-once-with-orig "barkrab") ; => ("barkrab" "arkra")
(sa/trim-both-once-with-orig "ab" "barkrab") ; => ("barkrab" "arkra")
(sa/trim-both-once-with-orig #{\a \b} "barkrab") ; => ("barkrab" "arkra")
(sa/trim-both-once-with-orig \a \a "aaxaa") ; => ("aaxaa" "axa")
Sequence of trimming steps
If there is a need for keeping all subsequent steps of trimming you can use the function trim-both-seq
. It works the same as trim-both
but returns a lazy sequence of strings, each being the result of next step of iterative trimming:
(require '[smangler.api :as sa])
(sa/trim-both-seq nil) ; => nil
(sa/trim-both-seq "abba") ; => ("abba", "bb", "")
(sa/trim-both-seq "some") ; => ("some")
(sa/trim-both-seq "barkrab") ; => ("barkrab" "arkra" "rkr" "k")
(sa/trim-both-seq "ab" "barkrab") ; => ("barkrab" "arkra" "rkr")
(sa/trim-both-seq #{\a \b} "barkrab") ; => ("barkrab" "arkra" "rkr")
(sa/trim-both-seq \a \a "aaxaa") ; => ("aaxaa" "axa" "x")
Low-level trimming
Certain applications may require more efficient and/or more strict trimming functions. It is particularly not recommended but there is a smangler.core
namespace which contains trimming operations which are a bit faster than those in API. They require certain argument types and no coercion is performed. The returned values may also differ when it comes to handling corner cases (nil
or empty values, not finding a match, etc.):
The function trim-both
from the namespace smangler.core
takes a string as its last (or only) argument and trims its characters on both ends are the same:
(require '[smangler.core :as c])
(c/trim-both nil) ; => nil
(c/trim-both "abcd") ; => "abcd"
(c/trim-both "abca") ; => "bc"
(c/trim-both "aabaa") ; => "b"
Optionally it can also take a matching function as the first argument. The function should take a character and return a character, nil
or false
. If a character passed as an argument to this matcher will be the first character of the passed string and if a value returned by this match will be equal to the last character of the passed string trimming will occur:
(require '[smangler.core :as c])
(c/trim-both #{\a \b} nil) ; => nil
(c/trim-both #{\a \b} "abba") ; => ""
(c/trim-both #(and (= % \a) %) "abba") ; => "bb"
(c/trim-both {\a \z} "abbz") ; => "bb"
Similarly to its counterpart from smangler.api
the function can handle 3 arguments. In this case the first two should be characters matching the beginning and the end of a string:
(require '[smangler.core :as c])
(c/trim-both \a \z nil) ; => nil
(c/trim-both \a \z "abbz") ; => "bb"
The function trim-both-once
from the namespace smangler.core
works the same way as trim-both
but trims the given string only once. However, it will return nil
instead of the original string if there is nothing to trim:
(require '[smangler.core :as c])
(c/trim-both-once nil) ; => nil
(c/trim-both-once "") ; => nil
(c/trim-both-once "aa") ; => ""
(c/trim-both-once "abba") ; => "bb"
(c/trim-both-once "some") ; => nil
(c/trim-both-once "barkrab") ; => "arkra"
(c/trim-both-once (set "ab") "barkrab") ; => "arkra"
(c/trim-both-once #{\a \b} "barkrab") ; => "arkra"
(c/trim-both-once (set "abcd") "xyz") ; => nil
(c/trim-both-once \a \a "aaxaa") ; => "axa"