clojure

Initializing elements of a map conditionally in Clojure


I'm looking for the best way to conditionally avoid adding an element to a map when it is being initialized/defined. In this case, I want to avoid adding an element to a map if the value for the key is nil.

(defn create-record [data]
  (let [res {
    :username (data :username)
    :first-name (get-in data [:user-info :name :first])
    :last-name (get-in data [:user-info :name :last])
    :gender (get-in data [:user-info :sex])
   }])
)

I don't want to add gender to the map if the results of the get-in are nil (the sex field in the data does not exist). Is there a way to do this when I create the map? I could remove all keys whose value is nil after I create the map but, in some cases, I want some keys to have nil values and others to not be in the map at all if they would have nil values.


Solution

  • I would use a combination of merge and when-let for these optional parameters.

    The core idea is to merge in either a single element map or nil for each of the optional parameters. Merging in nil will do nothing, so you won't see the nil in the map.

    (defn create-record [data]
      (let [res (merge {:username (data :username)
                        :first-name (get-in data [:user-info :name :first])
                        :last-name (get-in data [:user-info :name :last])}
                       (when-let [gender (get-in data [:user-info :sex])]
                         {:gender gender}))]
        res))
    

    Depending on how frequently you need to do this, I would recommend writing a short macro or function around the when-let to keep the code more concise.