How to convert a clojure keyword into a string? - clojure

In my application I need to convert clojure keyword eg. :var_name into a string "var_name". Any ideas how that could be done?

user=> (doc name)
-------------------------
clojure.core/name
([x])
Returns the name String of a string, symbol or keyword.
nil
user=> (name :var_name)
"var_name"

Actually, it's just as easy to get the namespace portion of a keyword:
(name :foo/bar) => "bar"
(namespace :foo/bar) => "foo"
Note that namespaces with multiple segments are separated with a '.', not a '/'
(namespace :foo/bar/baz) => throws exception: Invalid token: :foo/bar/baz
(namespace :foo.bar/baz) => "foo.bar"
And this also works with namespace qualified keywords:
;; assuming in the namespace foo.bar
(namespace ::baz) => "foo.bar"
(name ::baz) => "baz"

Note that kotarak's answer won't return the namespace part of keyword, just the name part - so :
(name :foo/bar)
>"bar"
Using his other comment gives what you asked for :
(subs (str :foo/bar) 1)
>"foo/bar"

It's not a tedious task to convert any data type into a string, Here is an example by using str.
(defn ConvertVectorToString []
(let [vector [1 2 3 4]]
(def toString (str vector)))
(println toString)
(println (type toString)
(let [KeyWordExample (keyword 10)]
(def ConvertKeywordToString (str KeyWordExample)))
(println ConvertKeywordToString)
(println (type ConvertKeywordToString))
(ConvertVectorToString) ;;Calling ConvertVectorToString Function
Output will be:
1234
java.lang.string
10
java.lang.string

This will also give you a string from a keyword:
(str (name :baz)) -> "baz"
(str (name ::baz)) -> "baz"

Related

Clojure: Name of variables in a list

I have a something like this:
(def a "text")
(def b "text")
(def c nil)
(def d 8)
(def e "")
(def testlist (list a b c d e ))
Now, is there any way to get the string of the variable names? I assume a 'no' is the most likely answer.
name does not seem to work, as it only returns the value. Does the list only contain the values after def?
EDIT: What i forgot and that may be essential to this question: i can neither use eval nor can i use defmacro, both are not allowed (for safety etc. reasons). So, yeah...
you could use macro to do this (just for fun. i don't think it is a viable usecase at all)
user> (defmacro list+ [& syms]
`(with-meta (list ~#syms) {:vars (quote ~syms)}))
#'user/list+
user> (def testlist (list+ a b c d e))
#'user/testlist
user> (-> testlist meta :vars)
(a b c d e)
user> (defn nil-vals-names [data]
(for [[v name] (map vector data (-> data meta :vars))
:when (nil? v)]
name))
#'user/nil-vals-names
user> (nil-vals-names testlist)
(c)
You will not be able to get a string from the variable names since Clojure will evaluate them as soon as possible to produce the testlist
=> (def testlist (a b c d e ))
("text" "text" nil 8 "")
However, you can quote the list to retrieve the symbol associated to each variable name
=> (def testlist (quote (a b c d e ))) ;; equivalent to '(a b c d e ))
(a b c d e)
And then transform these symbols into strings with the str function
=> (map str testlist)
("a" "b" "c" "d" "e")
Later, you can eval this list to retrieve the value in the context of your namespace
=> (map eval testlist)
("text" "text" nil 8 "")
Note that using eval with an external input (e.g. read-line) can create a security risk in Clojure and other languages.
Moreover, the list has to be evaluated in the same namespace as its definition. Otherwise, Clojure will not be able to resolve the symbols.
=> (ns otherns)
=> (map eval user/testlist)
java.lang.RuntimeException: Unable to resolve symbol: a in this context
The best practice in most case would be to use macros
It's quite unclear what are you trying to achieve, bit still there is a possible way.
meta function take a reference and returns a map with :name field that holds a sting with the variable name:
user=> (def foo 42)
#'user/foo
user=> (meta #'foo)
{:line 1, :column 1,
:file "/some/tmp/file.clj",
:name foo,
:ns #namespace[user]}

vector in clojure and type casting

(= ":bar:foo" ((fn [[a b]] (str b a)) [:foo :bar]))
I have several question about this clojure code.
what's the deal about : in front each element in vector?
How can str cast :foo to string type ":foo" ?
Thanks
In clojure, such element called as keywords. Keywords evaluate to themselves, and often used as accessors for the values.
(def x {:a 10, :b 20})
You can check the type:
user=> (class :foo)
clojure.lang.Keyword
user=> (type :foo)
clojure.lang.Keyword
You can convert it to str: Be cautious that : in the front.
user=> (str :foo)
":foo"
If you want to get only the name string from the keyword, then:
user=> (name :foo)
"foo"
Or you can create a keyword from str:
user=> (keyword "foo")
:foo

Clojure say-hi with varargs

Input: "Michael" "Julia" "Joe" "Sam"
Output: Hi, Michael, Julia, Joe, and Sam. (pay attention to the commas and the word "and")
Input: nil
Output: Hi, world.
Here is my first attempt:
(defn say-hi [& name]
(print "Hi," name))
user> (say-hi "Michael")
Hi, (Michael)
nil
user> (say-hi "Michael" "Julia")
Hi, (Michael Julia)
nil
Question:
How to implement default: (no input, say "Hi World!")
How to get rid of the parents around names in output?
How to implement the commas separation and add the conjunction word "and"?
First off, Clojure supports multi-arity functions, so you could do something like this to achieve default behaviour:
(defn say-hi
([] (say-hi "World"))
([& names] ...))
Then, what you want is to take a seq and join all the strings it contains together, using ", " in between. The clojure.string namespaces contains lots of string manipulation functions, one of them being clojure.string/join:
(require '[clojure.string :as string])
(string/join ", " ["Michael", "Julia"])
;; => "Michael, Julia"
But the last element of the seq should be concatenated using " and " as a separator, so you'll end up with something like this:
(require '[clojure.string :as string])
(defn say-hi
([] (say-hi "World"))
([& names]
(if (next names)
(format "Hi, %s, and %s!"
(string/join ", " (butlast names))
(last names))
(format "Hi, %s!" (first names)))))
Note that you have to differentiate between the single- and multi-name cases and (next names) basically checks whether the seq contains more than one element. (You could achieve the same by adding another arity to the function.)
(say-hi)
;; => "Hi, World!"
(say-hi "Michael")
;; => "Hi, Michael!"
(say-hi "Michael" "Julia" "Joe" "Sam")
;; => "Hi, Michael, Julia, Joe, and Sam!"
You can use clojure.string/join:
(use '[clojure.string :only [join]])
(defn sentencify [& elems]
(->>
[(join ", " (butlast elems)) (last elems)]
(remove empty?)
(join " and ")))
(defn say-hi [& name]
(print "Hi," (if name
(sentencify name)
"World!")))
A concise solution:
(defn say-hi [& names]
(let [names (case (count names)
0 ["world"]
1 names
(concat (butlast names) (list (str "and " (last names)))))]
(->> names, (cons "Hi"), (interpose ", "), (apply str))))
(say-hi)
;"Hi, world"
(say-hi "Michael")
;"Hi, Michael"
(say-hi "Michael" "Julia" "Joe" "Sam")
;"Hi, Michael, Julia, Joe, and Sam"
For long lists of names, you would want to eschew count, last, and butlast, maybe by pouring names into a vector first.
To print (as the question does) rather than return the formatted string, append print to the final form:
(->> names, (cons "Hi"), (interpose ", "), (apply str), print)

How can I read strings with backslashes?

Apparently I can't read-string some strings, like
user> (read-string "\" \\ABC \"")
RuntimeException Unsupported escape character: \A clojure.lang.Util.runtimeException (Util.java:219)
user>
Is there a way around that?
Thanks!
I assume that you want to end up with a string that when you print its "\ABC", so:
user=> (println "\\ABC")
\ABC
nil
As you see, the reader needs two "\". As read-string expects the string to be a valid Clojure expression and from your example you are trying to read a string that contains a string, you need to escape both the " (as you are doing) and the two \ :
user=> (def s (read-string "\" \\\\AB\""))
#'user/s
user=> (class s)
java.lang.String
user=> (println s)
\AB
nil
user=> s
" \\AB"

What is the right way to convert a namespaced clojure keyword to string?

When the name function is used it correctly returns the name of a keyword as a String, as in:
(name :k) ; => "k"
A problem exists when using name on a namespaced keyword such as:
(name :n/k) ; => "k"
I can use the namespace function to correctly obtain the string I'm looking for:
(str (namespace :n/k) "/" (name :n/k)) ; => "n/k"
But for some reason I feel there should be a better way to obtain the fully qualified string.
What would be the best way to do it?
Keywords actually store a symbol with the same namespace and name in a public final field and generate their string representations by prepending a colon to the string representation of that symbol. So, we can simply ask the symbol for the same and not prepend the colon:
(str (.-sym :foo/bar))
;= "foo/bar"
(subs (str :foo/k) 1)
;=> "foo/k"
Your approach is the best way to do it; it's only hard because converting a namespaced keyword to a string is an uncommon goal, and not something you'd expect to do regularly. You could write it without repeating the keyword, if you wanted:
(string/join "/" ((juxt namespace name) k))
Based on the implementation of name:
user=> (source name)
(defn name
"Returns the name String of a string, symbol or keyword."
{:tag String
:added "1.0"
:static true}
[x]
(if (string? x) x (. ^clojure.lang.Named x (getName))))
nil
user=>
And given that the clojure.lang.Named interface has a getNamespace method:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Named.java
So you can do this:
(defn full-name [k] (str (.getNamespace k) "/" (.getName k)))