clojure destructuring array into individual arguments - clojure

i am trying to do a multi insert using clojure.java.jdbc
the structure for multi-insert is something like this
(jdbc/multi-insert! db-spec :table_name
{:col1 "d1" :col2 "d2" :col3 "d3"}
{:col1 "d4" :col2 "d5" :col3 "d6"}...)
however i have the data in a vector like this
[
{:col1 "d1" :col2 "d2" :col3 "d3"}
{:col1 "d4" :col2 "d5" :col3 "d6"}
]
how do i destructure the array and pass the contents as individual arguments ?

You can use apply to call a function with a list of arguments.
> (str "Clo" "jure")
"Clojure"
> (apply str ["Clo" "jure"])
"Clojure"
In your case you want to apply jdbc/multi-insert! with a list containing db-spec, :table_name and all the maps from your existing vector.
(def data [{:col1 "d1" :col2 "d2" :col3 "d3"}
{:col1 "d4" :col2 "d5" :col3 "d6"}])
(apply
jdbc/multi-insert!
(conj [db-spec :table_name] data))
We're just using conj to create the argument list, then passing it to apply.
As Alex pointed out, the signature for apply actually allows you to pass inline arguments first, so long as the final argument is a list. You could rewrite that example without using conj.
(apply
jdbc/multi-insert!
db-spec :table_name data)

Related

Why `into` a map (`(into {} ,,,)`) works with `vectors` but not with `lists`, in Clojure?

I can build a map with a list of vectors:
user=> (into {} (list (vector "a" "b") (vector "c" "d")))
{"a" "b", "c" "d"}
But if I try to do it with a list of lists, it fails:
user=> (into {} (list (list "a" "b") (list "c" "d")))
Execution error (ClassCastException) at user/eval3 (REPL:1).
class java.lang.String cannot be cast to class java.util.Map$Entry (java.lang.String and java.util.Map$Entry are in module java.base of loader 'bootstrap')
Why?
When you do (into {} coll) it is the equivalent of (reduce conj {} coll) so each element of your coll is used as the second argument to conj with a hash map as the first argument.
conj is built into clojure.lang.RT and it calls (in this case) APersistentMap.cons() to add the element which is here: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L24
As you can see from that, if the argument is a MapEntry, it'll get added to the hash map as a key/value pair: (seq my-hash-map) produces a sequence of MapEntry items.
If the argument is a vector that has two elements, it'll get added as a key/value pair where the key is the first element of that vector and the value is the second element of that vector.
Otherwise, it tries to convert the argument to a sequence and then casts each element to MapEntry and adds it as a key/value pair.
When you pass a list of vectors (two-element vectors), they are added as key/value pairs per the second case above. When you pass a list of lists, the third case is invoked and it tries to cast each element of your inner list to a MapEntry -- which fails because those elements are String's.
Not a Clojure expert, but I think key-value pairs are always represented as two-element vectors.
E.g., going in the other direction:
(seq {:a :b :c :d})
; => ([:a :b] [:c :d])
I suspect the fn into relies on this, as judging by your error message it appears to be looking for the java.util.Map$Entry interface, and (presumably) without each tuple being a vector this check fails.

why this partial not working

Here is my code:
(def partial-join (partial (clojure.string/join ",")))
=>(clojure.string/join "," ["foo" "bar"])
"foo,bar"
=> (partial-join ["foo" "bar"])
And it raises this exception:
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn .repl/eval12557 (form-init2162333644921704923.clj:1)
See the doc of clojure.string/join.
clojure.string/join
([coll] [separator coll])
Returns a string of all elements in coll, as returned by (seq coll),
separated by an optional separator.
when only one argument is provided for clojure.string/join, this function regard its argument as collection, so:
user=> (clojure.string/join ",")
","
Next, see the doc of partial.
clojure.core/partial
([f] [f arg1] [f arg1 arg2] [f arg1 arg2 arg3] [f arg1 arg2 arg3 & more])
Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args.
When only one argument provided, partial returns its argument.
user=> (partial (clojure.string/join ","))
","
Try this:
user=> (def partial-join (partial clojure.string/join ","))
#'user/partial-join
user=> (partial-join ["a" "b"])
"a,b"
The problem is NOT with the number of parameters passed to clojure.string/join. The problem is the brackets surrounding clojure.string/join call the function and the result of the function call is passed to partial. What you want to do is pass the function and the first param to partial as below:
(def partial-join (partial clojure.string/join ","))
(partial-join ["foo" "bar"])
;; => "foo,bar"

Arity exception deref'ing promise

I'm using the http-kit library to make some webcalls and it returns a promise for each.
When I try to deref any of the promises in the vector I get the following error
ArityException Wrong number of args (1) passed to: core/eval5473/fn--5474 clojure.lang.AFn.throwArity (AFn.ja
va:429)
Simplest way to reproduce in a repl without http-kit is as follows
Create collection
(def x [ [1 (promise)] [2 (promise)] [3 (promise)]])
Simple Test
(map first x)
;user=> (1 2 3)
My Test
(map #(vector % #%2) x)
;user=> ArityException Wrong number of args (1) passed to: user/eval109/fn--110 clojure.lang.AFn.throwArity (AFn.java
:429)
Update
I should probably delete this question. The problem had nothing to do with promises as Valentin noted below.
I was typing %2 and thinking second argument. When what i needed was #(second %). i.e second entry in first and only argument.
The function that is the second argument of map must accept only 1 argument in this case (which is meant to be an element of the seq that is being walked through).
You seem to be mistaking passing 2 arguments to a function and passing 1 argument that is a vector of 2 elements.
What you want to write is
(map (fn [[a b]] (vector a #b)) x)
...whereas what you're currently writing is equivalent to:
(map (fn [a b] (vector a #b)) x)
So this is not a problem about promises in fact.

How to apply values to multiple functions in Clojure?

Basically, I need to do something like map, but instead of applying a function to all elements in a collection, I need to apply the same (set of) value(s) to a collection of functions (does this operation have a name?). This might seem like a simple question, but I haven't found an idiomatic way to do it in Clojure. For the special case where I need to apply only one value to each function, for example, I have used
(for [f funs] (f value))
where value is, of course, the value I need each function to take as an argument, and funs is the collection of functions which need to be called with value as the argument.
My question is, then, is there a function in Clojure that does this, but is also generalised for arbitrary numbers of arguments? Or is the above indeed idiomatic Clojure?
You're looking for juxt
juxt
Takes a set of functions and returns a fn that is the juxtaposition
of those fns. The returned fn takes a variable number of args, and
returns a vector containing the result of applying each fn to the
args (left-to-right).
((juxt a b c) x) => [(a x) (b x) (c x)]
From a section of CLOJURE for the BRAVE and TRUE
Another fun thing you can do with map is pass it a collection of
functions. You could use this if you wanted to perform a set of
calculations on different collections of numbers, like so:
(def sum #(reduce + %))
(def avg #(/ (sum %) (count %)))
(defn stats
[numbers]
(map #(% numbers) [sum count avg]))
(stats [3 4 10])
; => (17 3 17/3)
(stats [80 1 44 13 6])
; => (144 5 144/5)

First-class class/record instantiation

I have a sequence of records/classes, and I map over that sequence with new and expect to get a sequence of instances of those records/classes. I know new is a special form, but I was expecting Clojure to do The Right Thing in this case.
But this does not work:
(map new [SomeClass1 SomeClass2 SomeClass3])
Neither does this.
(map #(new %) [SomeClass1 SomeClass2 SomeClass3])
Similar code works in Factor.
{ SomeClass1 SomeClass2 SomeClass3 } [ new ] map
What would be the right way to do this in Clojure? (I expect it won't involve Class.newInstance ugliness.)
Edit:
The following works, but is perhaps slower than necessary. (I don't know for sure. I would appreciate some information on this.)
(map #(eval `(new ~%)) [SomeClass1 SomeClass2 SomeClass3])
Also I am looking for something more elegant.
because special-formes are well... special they are not first class and don't compose as proper functions do, you can solve this with eval and macros:
a solution using eval:
(defn fnew [c] (eval `(new ~c)))
hello.core> (map fnew ['Exception 'java.lang.String])
(#<Exception java.lang.Exception> "")
and a version that takes arguments to the constructors:
(defn fnew [c] (eval `(new ~#c)))
hello.core> (map fnew ['(Exception) '(java.lang.String "I am a contructor argument")])
(#<Exception java.lang.Exception> "I am a contructor argument")
(map fnew [ [Exception] [(class "foo") "I am a contructor argument"]])
(#<Exception java.lang.Exception> "I am a contructor argument")
and here is a macro example
hello.core> (defmacro newify [classes] (apply vector (for [c classes] `(new ~c))))
#'hello.core/newify
hello.core> (macroexpand '(newify [java.lang.String Exception]))
[(new java.lang.String) (new Exception)]
hello.core> (newify [java.lang.String Exception])
["" #<Exception java.lang.Exception>]
The macro version is likely more efficient while the eval version is more flexible.
As new is a special form, one of the solution to make it work like first class would be use the clojure low level calls like:
(map #(clojure.lang.Reflector/invokeConstructor %1 (into-array [])) [String])
This may lead to the performance problems of Reflection hence the macro based solution are prefered over this.