Идиоматический способ "обновления" данного ключа в карте Clojure?

скажем, у нас есть карта, которая выглядит примерно так:

(def m {:a {:foo "bar"}})

теперь мы хотели бы обновить ключ: a в m с некоторыми новыми значениями:

(def vs {:baz "qux"})

если бы это был Python, мы могли бы сделать что-то вроде:

>>> d = {'a': {'foo': 'bar'}}
>>> d['a'].update({'baz': 'qux'})
>>> d
{'a': {'foo': 'bar', 'baz': 'qux'}}

самым простым эквивалентом Clojure, который я нашел, было определение такой функции:

(defn update-key
  "
  Updates a given key `k` over a map `m` with a map of values `vs`.
  "
  [k m vs]
  (assoc m k (merge (k m) vs)))

который затем вызывается как:

(update-key :a m vs)
; => {:a {:foo "bar" :baz "qux"}}

Итак, мой вопрос: Какой самый идиоматический и правильный способ достичь того же функциональность как update() метод Python дикты обеспечивают?

1 ответов


Я думаю, вы ищете assoc-in:

(def m {:a {:foo "bar"}})

(assoc-in m [:a :foo] "qux")
; => {:a {:foo "qux"}}

(assoc-in m [:a :baz] "qux")
; => {:a {:foo "bar", :baz "qux"}}

update-in похоже, и, возможно, стоит посмотреть тоже. Это может быть ближе к вашему примеру Python:

(def m {:a {:foo "bar"}})
(def vs {:baz "qux"})

(update-in m [:a] merge vs)
; => {:a {:foo "bar", :baz "qux"}}

обновление:

даже если ключ является значением переменной (а не константой времени компиляции), вы все равно можете использовать оба update-in и assoc-in, поместив переменную в вектор:

(def m {:a {:foo "bar"}})
(def k' :baz)
(def v' "qux")

(assoc-in m [:a k'] v')
; => {:a {:foo "bar", :baz "qux"}}

вы также можете создать the ключи векторные программно:

(def m {:a {:foo "bar"}})
(def k' :baz)
(def v' "qux")

(let [ks (conj [:a] k')]
  (assoc-in m ks v'))
; => {:a {:foo "bar", :baz "qux"}}