3
votes

Suppose I have a map like so

{:a 1 :b 2 :c 3}

I'd like to map over this like so (note - non-working pseudocode):

(mapcat (fn [[:key key-a][:value value-a]] (println "key: " key-a "\n value: " value-a )))

Is this possible without getting the keys of the function first, mapping over those and reading them back from the function?

My question is: How do you destructure a map into key-value pairs without knowing the keys in Clojure?

2

2 Answers

7
votes
(seq {:a 1 :b 2 :c 3})
;([:a 1] [:b 2] [:c 3])

Calling seq on a map gives you a sequence of key-value pairs. The seq call is usually implicit.

The pairs

  • need not be in the entered order;
  • are actually MapEntrys, which behave as pairs.

Thus

(type (first {:a 1 :b 2 :c 3}))
;clojure.lang.MapEntry

Your pseudocode

(mapcat (fn [[:key key-a][:value value-a]] (println "key: " key-a "\n value: " value-a )))

... needs several repairs:

  • Supply the omitted final argument - the collection to which map is applied.
  • Simply destructure each MapEntry as a pair to get at
    key and value.
  • Use map instead of mapcat to apply the function to each pair. It's just lucky that mapcat works at all.
  • Use dorun to force the sequence to evaluate and to throw it away as it does so. The REPL does the former for you, but a running application need not.

This gives us

(dorun
 (map 
  (fn [[key-a value-a]] (println "key: " key-a " value: " value-a ))
  {:a 1 :b 2 :c 3}))

Which prints

key:  :a  value:  1
key:  :c  value:  3
key:  :b  value:  2

... and returns nil.

3
votes

When you map over a map, each element is a vector containing two values: the key and the value. You destructure each element with a vector like this:

(def m {:a 1 :b 2 :c 3})
(map (fn [[key value]] (str "key: " key " > value: " value-a)) m)

Note that the function passed to map returns a String value, instead of your call to println, since we're trying to transform the collection passed in by applying a function to each value. You could pass the collection returned by map to prn to debug the value, though the REPL will print it out for you anyway.