Calling java.lang.String .split methode in clojure - clojure

When i call the .split methode in clojure i get: #object["[Ljava.lang.String;" 0x5aaf6982 "[Ljava.lang.String;#5aaf6982"] #object["[Ljava.lang.String;" 0x18fbbda2 "[Ljava.lang.String;#18fbbda2"].
How can i use this object in my code?

You can use vec to convert a Java array into a vector, e.g.
(vec (.split "1,2,3,4,5" ","))
=> ["1" "2" "3" "4" "5"]
But really if you want to split a string into a Clojure collection you should be using clojure.string/split instead:
(clojure.string/split "1,2,3,4,5" #",")
=> ["1" "2" "3" "4" "5"]

Assuming that you are using .split method of Java String class, then the result is a Java array.
You can use aget function of Clojure to access the elements.
And depending on your use case, it might be better to convert the array into Clojure sequence, so you can use a vast array of functions that Clojure provides to manipulate sequences.

Related

Why is the hash map get returning nil after passing through the hash map as a function argument?

I am very new to clojure so this may have a simple fix. I have two examples of code that I can't seem to find the difference in why one works and the other doesn't. The first is:
(defn example []
(def demokeys (hash-map "z" 1 "b" 2 "a" 3))
(println demokeys)
(println (get demokeys "b")))
(example)
which is from https://www.tutorialspoint.com/clojure/clojure_maps_get.htm. This works the way I expect it to, with it printing the hash map and then a 2 on the next line.
The second example is:
(defn bill-total [bill]
(println bill)
(println (get bill "b"))
(println "sometext")
)
(bill-total[(hash-map "z" 1 "b" 2 "a" 3)])
which prints the hashmap, then nil, then sometext. Why does it not correctly print the 2 as it does in the previous example?
First of all, don't use def within defn unless you need dynamic runtime declarations (and even then, you should probably reconsider). Instead, use let for local bindings.
As to your main question, you have wrapped the map in a vector, with those [] here: (bill-total [...]). When calling a function, you don't need to repeat its arguments vector's brackets - call it just like you call println or hash-map, because they're also just regular functions.
So in the end, it should be (bill-total (hash-map "z" 1 "b" 2 "a" 3)).
As a final note, there's rarely a need to explicitly use hash-map, especially when you already know the keys and values. So, instead of (hash-map ...) you can use the map literal and write {...}, just like in {"z" 1, "b" 2, "a" 3} (I used commas here just for readability since Clojure ignores them).

Replace string with a regex containing a variable without macro

I need to delete some contents at the end of a string (eq_code) using Clojure.
I want to implement a regex initialized by a variable (get-in vector [3 1]).
Maybe macro would have helped me.
Here is the code :
(reset! atom_code (clojure.string/replace eq_code #(str (get-in vector [3 1])) ""))
The error is :
IllegalArgumentException Invalid match arg: project.core$interpreted_lang_while$fn__4457#7ac4b7c5 clojure.string/replace (string.clj:102)
Is there a way to replace a substring without using macro ?
For example using a function that return a regex.
https://clojuredocs.org/clojure.string/replace
replace match parameter (second arg) can't be a function.
The solution is to construct the pattern from your dynamic value:
user> (def data [["a" "b"] ["c" "d"]])
#'user/data
user> (clojure.string/replace "mama" (re-pattern (get-in data [0 0])) "")
"mm"
and also: vector is the core function, so try not to shadow it by using it as a var name (though it's not actually what is wrong with your solution)
I think the problem is that vector is a clojure function, and I am guessing that you have some data named vector that is causing the compiler confusion.
Could you please update your question with sample data and desired output?

Creating a Clojure macro that uses a string to call a java function

So I'm trying to make a Clojure macro that makes it easy to interop with Java classes utilizing the Builder pattern.
Here's what I've tried so far.
(defmacro test-macro
[]
(list
(symbol ".queryParam")
(-> (ClientBuilder/newClient)
(.target "https://www.test.com"))
"key1"
(object-array ["val1"])))
Which expands to the below
(.
#object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "org.glassfish.jersey.client.JerseyWebTarget#107a5073"]
queryParam
"key1"
#object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;#16751ba2"])
The desired result is:
(.queryParam
#object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "org.glassfish.jersey.client.JerseyWebTarget#107a5073"]
"key1"
#object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;#16751ba2"])
I guess the . is causing something to get evaluated and moved around? In which case the solution would to be to quote it. But how can I quote the results of an evaluated expression?
My goal is to convert maps into code that build the object by have the maps keys be the functions to be called and the values be the arguments passed into the Java functions.
I understand how to use the threading and do-to macros but am trying to make request building function data driven. I want to be able take in a map with the key as "queryParam" and the values as the arguments. By having this I can leverage the entirety on the java classes functions only having to write one function myself and there is enough of a 1 to 1 mapping I don't believe others will find it magical.
(def test-map {"target" ["https://www.test.com"]
"path" ["qa" "rest/service"]
"queryParam" [["key1" (object-array ["val1"])]
["key2" (object-array ["val21" "val22" "val23"])]] })
(-> (ClientBuilder/newClient)
(.target "https://www.test.com")
(.path "qa")
(.path "rest/service")
(.queryParam "key1" (object-array ["val1"]))
(.queryParam "key2" (object-array ["val21" "val22" "val23"])))
From your question it's not clear if you have to use map as your builder data structure. I would recommend using the threading macro for working directly with Java classes implementing the builder pattern:
(-> (ClientBuilder.)
(.forEndpoint "http://example.com")
(.withQueryParam "key1" "value1")
(.build))
For classes that don't implement builder pattern and their methods return void (e.g. setter methods) you can use doto macro:
(doto (Client.)
(.setEndpoint "http://example.com")
(.setQueryParam "key1" "value1"))
Implementing a macro using a map for encoding Java method calls is possible but awkward. You would have to keep each method arguments inside a sequence (in map values) to be a able to call methods with multiple parameters or have some convention for storing arguments for single parameter methods, handling varargs, using map to specify method calls doesn't guarantee the order they will be invoked etc. It will add much complexity and magic to your code.
This is how you could implement it:
(defmacro builder [b m]
(let [method-calls
(map (fn [[k v]] `(. (~(symbol k) ~#v))) m)]
`(-> ~b
~#method-calls)))
(macroexpand-1
'(builder (StringBuilder.) {"append" ["a"]}))
;; => (clojure.core/-> (StringBuilder.) (. (append "a")))
(str
(builder (StringBuilder.) {"append" ["a"] }))
;; => "a"

Clojure: Unable to find static field

Given the following piece of code:
(map Integer/parseInt ["1" "2" "3" "4"])
Why do I get the following exception unless I wrap Integer/parseInt in an anonymous function and call it manually (#(Integer/parseInt %))?
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer
the documentation on java interop says the following:
The preferred idiomatic forms for accessing field or method members
are given above. The instance member form works for both fields and
methods. The instanceField form is preferred for fields and required
if both a field and a 0-argument method of the same name exist. They
all expand into calls to the dot operator (described below) at
macroexpansion time. The expansions are as follows:
...
(Classname/staticMethod
args*) ==> (. Classname staticMethod args*) Classname/staticField ==>
(. Classname staticField)
so you should remember, that Class/fieldName is just a sugar for getting a static field, neither static method call, nor reference to the static method (java method is not a clojure function really), so there is no static field parseInt in Integer class, while (Class/fieldName arg) calls a static method, they are two totally different operations, using the similar sugary syntax.
so when you do (map #(Integer/parseInt %) ["1" "2" "3" "4"]) it expands to
(map #(. Integer parseInt %) ["1" "2" "3" "4"])
(you can easily see it yourself with macroexpansion),
and (map Integer/parseInt ["1" "2" "3"]) expands to
(map (. Integer parseInt) ["1" "2" "3"])
It fails when it is trying to get a field (which you think is getting a reference to a method).
Integer/parseInt is a static method of Integer class, not a clojure function. Each clojure function is compiled to java class which implements clojure.lang.IFn interface. map expects clojure function (which implements IFn interface) as a first argument, however, Integer/parseInt is not.
You can check that in the clojure repl.
user=> (type map)
clojure.core$map
user=> (type Integer)
java.lang.Class
user=> (type Integer/parseInt)
CompilerException java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer, compiling:(/private/var/folders/p_/psdvlp_12sdcxq07pp07p_ycs_v5qf/T/form-init4110003279275246905.clj:1:1)
user=> (defn f [] 1)
#'user/f
user=> (type f)
user$f
user=> (type #(1))
user$eval9947$fn__9948
Perhaps reading this stackoverflow question will help you understand what is going on.
You might prefer to do it without the Java interop:
(map read-string ["1" "2"])
or for a more safe variant:
(map clojure.edn/read-string ["1" "2"])
I personally prefer to minimize the use of Java as much as possible in Clojure code.
As for why you can't just pass the Java function, because map expects a function in Clojure, not a function in Java.

Can I look up a string key in a hashmap with convenient syntax?

Being able to access key of (def my-hashmap {:one 1}) with (:one my-hashmap) is very convenient. Sometimes I have hashmaps that have string values.
I want to be able to be able to access (def my-hashmap {"one" 1}) with syntax like ("one" my-hashmap). Obviously, I can't. My options are either use (get my-hashmap "one") or to transform the hashmap so it has symbols for keys. I'd rather not, as I'll be passing the object to other functions which might expect it to be in the original format.
What is the shortest way I can look up a string key? Is get the only way or is there some magic?
The map can be used as a function
(my-hashmap "one")
If you're already passing the map as a function parameter or let-bound variable, you can also do destructuring of a map with string keys using the :strs keyword:
(let [{:strs [a]} {"a" 1, "b" 2}]
a)
; => 1