I am currenly using a function
(def mymap {})
(defn function1 [var1 var2 var3 var4 var5]
;calls another functions with all variables.
(function2 var1 var2 var3 var4 var5)
)
But as this is having more parameters I would like to convert this to a map before calling functions2.
(function2((assoc mymap (keyword var1) var1
(keyword var2) var2
(keyword var3) var3
(keyword var4) var4
(keyword var5) var5 ))
)
Is this the correct way? Do we have better way to do this(In java we direclty use some objects in this scenario)
For general function args, I always go in order from the biggest to the smallest, in either size or "importance" (somewhat subjective).
However, if you have more than 3 args, I prefer to pass a map containing the args and appropriate keyword names.
The Tupelo Clojure library has some tools to make this easy. The macro vals->map
takes multiple variable names and constructs a map from the (keywordized) variable name to its value, like:
(let [ctx (let [a 1
b 2
c 3
d 4
e 5]
(t/vals->map a b c d e))]
(is= ctx {:a 1 :b 2 :c 3 :d 4 :e 5})
The macro with-map-vals
does the opposite, deconstructing map values into local variables named for their keys. It is similar to the Clojure :keys
destructuring, but in (IMHO) a more natural form:
(let [{:keys [a b c d e]} ctx] ; Clojure destructing syntax
(is= [a b c d e] [1 2 3 4 5]))
(t/with-map-vals ctx [a b c d e]
(is= [a b c d e] [1 2 3 4 5])
(is= 15 (+ a b c d e)))
(t/with-map-vals ctx [b a d c e] ; order doesn't matter
(is= [a b c d e] [1 2 3 4 5])
(is= 15 (+ a b c d e)))
(t/with-map-vals ctx [b a d] ; can ignore stuff you don't care about
(is= [d a b] [4 1 2]))
(throws?
(t/with-map-vals ctx [a b z] ; throws if key doesn't exist
(println "won't ever get here")))))
If you have nested data in maps and/or vectors, you can use the more powerful destruct
and restruct
tools. Here is a brief example (from the unit tests):
(let [info {:a 1
:b {:c 3
:d 4}}
mania {:x 6
:y {:w 333
:z 666}}]
(t/it-> (t/destruct [info {:a ?
:b {:c ?
:d ?}}
mania {:y {:z ?}}] ; can ignore unwanted keys like :x
(let [a (+ 100 a)
c (+ 100 c)
d z
z 777]
(restruct-all)))
(t/with-map-vals it [info mania]
(is= info {:a 101, :b {:c 103, :d 666}})
(is= mania {:x 6, :y {:w 333, :z 777}})))
As you can see, a question mark ?
will cause the corresponding value to be destructed into a variable named for the corresponding keyword. It is also possible to create explicit variable names like so:
(dotest
(let [info {:a 777
:b [2 3 4]}
mania [{:a 11} {:b 22} {:c [7 8 9]}]]
(let [z ::dummy]
(t/it-> (t/destruct [info {:a z
:b [d e f]}
mania [{:a ?} BBB {:c clutter}]]
(let [clutter (mapv inc clutter)
BBB {:BBB 33}
z 77
d (+ 7 d)]
(restruct-all)))
(t/with-map-vals it [info mania]
(is= info {:a 77, :b [9 3 4]})
(is= mania [{:a 11} {:BBB 33} {:c [8 9 10]}]))))))
It also works for mixed maps & vectors:
(let [data {:a [{:b 2}
{:c 3}
[7 8 9]]} ]
(t/destruct [data {:a [{:b p}
{:c q}
[r s t]]} ]
(is= [2 3 7 8 9] [p q r s t])))