在Clojure地图中“更新”给定键的习惯方法?

时间:2013-03-19 22:45:01

标签: map clojure merge

假设我们的地图看起来像:

(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"}}

所以我的问题是:实现与Python dicts提供的update()方法相同功能的最惯用和最正确的方法是什么?

1 个答案:

答案 0 :(得分:11)

我认为您正在寻找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-inassoc-in

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

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

您还可以以编程方式构建向量:

(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"}}