Search code examples
clojure

Inverse process of :keys destructuring: construct map from sequence


The more I write in Clojure, the more I come across the following sort of pattern:

(defn mapkeys [foo bar baz]
   {:foo foo, :bar bar, :baz baz})

In a certain sense, this looks like the inverse process that a destructuring like

(let [{:keys [foo bar baz]}] ... )

would achieve.

Is there a "built-in" way in Clojure to achieve something similar to the above mapkeys (mapping name to keyword=>value) - perhaps for an arbitrary length list of names?


Solution

  • No such thing is built in, because it doesn't need to be. Unlike destructuring, which is fairly involved, constructing maps is very simple in Clojure, and so fancy ways of doing it are left for ordinary libraries. For example, I long ago wrote flatland.useful.map/keyed, which mirrors the three modes of map destructuring:

    (let [transforms {:keys keyword
                      :strs str
                      :syms identity}]
      (defmacro keyed
          "Create a map in which, for each symbol S in vars, (keyword S) is a
      key mapping to the value of S in the current scope. If passed an optional
      :strs or :syms first argument, use strings or symbols as the keys instead."
        ([vars] `(keyed :keys ~vars))
        ([key-type vars]
           (let [transform (comp (partial list `quote)
                                 (transforms key-type))]
             (into {} (map (juxt transform identity) vars))))))
    

    But if you only care about keywords, and don't demand a docstring, it could be much shorter:

    (defmacro keyed [names]
      (into {}
            (for [n names]
              [(keyword n) n])))