Вы можете использовать деструктурирование в let
:
(let [{:keys [a b c]} {:a 1 :b false}]
[a b c])
;; => [1 false nil]
или в аргументах функции:
(defn args-demo [{:keys [a b c]}]
[a b c])
(args-demo {:a 1 :b false})
;; => [1 false nil]
Проблема заключается в том, что он связывается с nil
если конкретный ключ отсутствует на карте.Если ваши значения могут иметь значения nil
, это не сработает.
Вы можете использовать некоторые значения «маркера» для отсутствующих значений:
(let [{:keys [a b c] :or {a ::absent b ::absent c ::absent}} {:a 1 :b nil}]
(cond-> {}
(not= a ::absent) (assoc :a2 a)
(not= b ::absent) (assoc :b2 b)
(not= c ::absent) (assoc :c2 c)))
;; => {:a2 1, :b2 nil}
Вы также можете создать макрос:
(defmacro when-key-present
[k m & body]
`(let [{:keys [~k] :or {~k ::not-found}} ~m]
(when (not= ~k ::not-found)
~@body)))
(when-key-present a {:a false :b nil}
(println "a was" a))
;; prints "a was false"
(when-key-present b {:a false :b nil}
(println "b was" b))
;; prints "b was nil"
(when-key-present c {:a false :b nil}
(println "c was" c))
;; doesn't print anything
И ваша функция станет такой:
(defn reader-options [opts]
(let [builder (CsvReadOptions$Builder.)]
(when-key-present locale opts
(.locale builder locale))
(when-key-present header opts
(.header builder header))
(.build builder)))
Вы можете пойти дальше и создать макрос, который бы предполагал, что ключ в карте опций идентичен методу построителя, которыйдолжен вызываться так, чтобы вы могли затем использовать его следующим образом:
(let [builder (CsvReadOptions$Builder.)]
(doseq [k (keys opts)]
(set-builder-property builder k opts)))
, но это становится все более и более волшебным и трудным для понимания, поэтому я подумал бы дважды, прежде чем прибегать к макросам.