I have a function that takes in a string s
and a map of characters charmap
. If any characters in the string s
are inside of charmap
, replace the character with the value of the map.
Note, the keys in the map must be a string, rather than a char.
For example:
(replace-characters "Hi!" {"!" "Exclamation Mark"}) ;; => "HiExclamation Mark"
This is the code I am currently using:
(defn- replace-characters
"Replaces any characters in a string that are mapped in the given charmap"
[s charmap]
(let [character-set (set s)
characters (filter #(contains? charmap (str %)) character-set)]
(string/replace s (re-pattern (string/join "|" characters)) charmap)))
However, I am getting a NullPointerException
and I am seriously confused on why.
java.lang.NullPointerException: Cannot invoke "String.indexOf(int)" because "s" is null
I would like to solve this in pure Clojure preferably, not Java, etc.
Update: So the code above works in the repl. Which is nice. But for some reason the following is causing the error:
(-> s
(string/replace #"[:/?#\[\]@!$&'()* ,;=]" "")
(replace-characters charmap)) ;; Where charmap is a large map of key value characters.
CodePudding user response:
This is the expression that causes error:
(str/replace "Hi" #"" {"!" "Exclamation Mark"})
("!"
was replaced with regex to ""
, character-set
has value #{\H \i}
and characters
is ()
, so pattern created with re-pattern
is #""
.)
Empty regex matches every space between letters:
(str/replace "Hi" #"" " ")
=> " H i "
So, replace
is looking for replacement in hash-map {"!" "Exclamation Mark"}
, but doesn't find anything- there is no key ""
:
(str/replace "Hi" #"" {"!" "Exclamation Mark"})
=> error
(str/replace "Hi" #"" {"" " "})
=> " H i "
One possible solution can be to simplify definition of replace-characters
(this solution will work only for non-empty charmap
):
(defn replace-characters [s charmap]
(str/replace s (re-pattern (str/join "|" (keys charmap)))
charmap))
Tests:
(replace-characters "Hi!" {"!" "Exclamation Mark"})
=> "HiExclamation Mark"
(-> "Hi!"
(str/replace #"[:/?#\[\]@!$&'()* ,;=]" "")
(replace-characters {"!" "Exclamation Mark"}))
=> "Hi"
CodePudding user response:
i would just go with something as simple as this:
(apply str (replace {"!" "[exclamation mark]"
"?" "[question mark]"}
(map str "is it possible? sure!")))
;;=> "is it possible[question mark] sure[exclamation mark]"
or this way with transducers:
(apply str (eduction (map str) (replace {"!" "[exclamation mark]"
"?" "[question mark]"})
"is it possible? sure!"))
or maybe like this:
(defn replace-characters [s rep]
(apply str (map #(rep (str %) %) s)))
user> (replace-characters "is it possible? sure!" {"!" "[exclamation mark]"
"?" "[question mark]"})
;;=> "is it possible[question mark] sure[exclamation mark]"
CodePudding user response:
string/escape
does exactly what you want:
(string/escape "Hi!" {\! "Exclamation Mark"})
;; => "HiExclamation Mark"
that function replaces characters using a map from character to replacement.
If you want to replace regexes or string you can use reduce-kv
in combination with string/replace
:
(def replacements
(array-map
"!" "Exclamation Mark"
#"[:/?#\[\]@!$&'()* ,;=]" ""
#_more_replacements))
(defn replace [s replacements]
(reduce-kv string/replace s replacements))
So you loop (in order, when replacements
is an array-map
) over the key and values of the replacements and apply them on the string s
using string/replace
.
(replace "Hi!" replacements)
;; => "HiExclamation Mark"
if the order of the elements in the array-map
is reversed the substitution of the ! with ""
(since ! is in the regex) would succeed:
(replace "Hi!" (into (array-map) (reverse replacements)))
;; => "Hi"