1

I have a question regarding two functions, one taking a complete map and the other specific keywords like so:

(def mapVal
 {:test "n"
  :anotherKey "n"})

(defn functionTest
 [& {:keys [test anotherKey] :or {:test "d" :anotherKey "d"}}]
 (println :test :anotherKey))

(defn mapFunc
 [map]
 (functionTest (get-in map [:test :anotherKey])))

The goal would be that all the keys in the parameter map will be passed correctly to the functionTest. Is there anyway this could work? I tried a few things but i just cant get all the keywords and values passed to the functionTest. What i dont want is just the values of the map, it should be passed to the other function with keyword and value.

Jonathan
  • 11
  • 2

2 Answers2

2

You're pretty close. A few things should clear it up.

First, when you declare parameters with [& varname] that means varname will be a list containing all the extra parameters. So you don't need to use that '&' here to destructure the input. Instead, you just name which keys you want to have become variables.

Try this:

(defn functionTest
 [{:keys [test anotherKey]}]
 (println test anotherKey))

And the other problem is using get-in. With get-in you're defining a "path" through nested data structures with that vector. For example, given:

{:first {:a 1 :b 2} :second {:c 3 :d 4}}

You could use get-in to get the value at :second :c with this:

(get-in {:first {:a 1 :b 2} :second {:c 3 :d 4}} [:second :c])

In your case, you don't need to use get-in at all. You just need to pass the entire map. The destructuring you defined in functionTest will handle the rest. Here is what I did that worked:

(defn mapFunc
 [map]
 (functionTest map))

I would also suggest you not name the that variable 'map' since it conflicts with the map function.

Thumbnail
  • 13,293
  • 2
  • 29
  • 37
robbie.huffman
  • 423
  • 4
  • 12
1

get-in is for accessing nested associative data structures.

(def m {:a {:x 1}})
(get-in m [:a :x]) ;;=> 1

After you destructure a map those values are in scope and are accessed via symbols. Your example should look like this:

(def mapVal
  {:test "n"
  :anotherKey "n"})

(defn functionTest
  [& {:keys [test anotherKey]
      :or {:test "d" :anotherKey "d"}}]
  (println test anotherKey))

(defn mapFunc
  [m]
  (apply functionTest (apply concat (select-keys m [:test :anotherKey]))))


(mapFunc mapVal) ;;=> prints: n n

You have to go through this because functionTest is accepting bare key value pairs as optional parameters (the ones to the right of the &), as in:

(functionTest :test "n"
              :anotherKey "n" )
;;=> Also prints: n n

select-keys returns a map with only the specified keys:

(select-keys mapVal [:test])
;; => {:test "n"}

applying concat to a map returns a flat seq of keys and values:

(apply concat (select-keys mapVal [:test :anotherKey]))
;; => (:test "n" :anotherKey "n")

apply applies a function to a seq, as though the seq were its argument list:

(+ [1 2 3]) ;;=> Error.
(apply + [1 2 3]) ;; => 6

As a side note, conventionally in Clojure code, snake-case is preferred to camelCase for most names.

KGZM
  • 414
  • 3
  • 5