如何在Clojure中更新矢量项?

scr*_*tty 12 clojure

鉴于:

(def my-vec [{:id 0 :a "foo" :b "bar"} {:id 1 :a "baz" :b "spam"} 
             {:id 2 :a "qux" :b "fred"}])
Run Code Online (Sandbox Code Playgroud)

如何以惯用方式更新my-vec中的项目以:id=1获取值:a="baz2":b="spam2"

*:我认识到我实际上不会更新my-vec,但实际上返回了一个与my-vec相同的新向量,除了替换值.

Bri*_*per 10

您是否提前知道id == 1的地图是矢量中的第二张地图?如果是这样:

user> (-> my-vec
          (assoc-in [1 :a] "baz2")
          (assoc-in [1 :b] "spam2"))
[{:id 0, :a "foo", :b "bar"} {:id 1, :a "baz2", :b "spam2"} {:id 2, :a "qux", :b "fred"}]
Run Code Online (Sandbox Code Playgroud)

如果你需要通过id访问你的数据,另一个想法是用键入的哈希映射的哈希映射替换你的哈希映射向量:id.然后,assoc-in无论事物的顺序如何,你都可以更轻松.

user> (def new-my-vec (zipmap (map :id my-vec) my-vec))
#'user/new-my-vec
user> new-my-vec
{2 {:id 2, :a "qux", :b "fred"}, 1 {:id 1, :a "baz", :b "spam"}, 0 {:id 0, :a "foo", :b "bar"}}
user> (-> new-my-vec
          (assoc-in [1 :a] "baz2")
          (assoc-in [1 :b] "spam2"))
{2 {:id 2, :a "qux", :b "fred"}, 1 {:id 1, :a "baz2", :b "spam2"}, 0 {:id 0, :a "foo", :b "bar"}}
Run Code Online (Sandbox Code Playgroud)

  • `(update-in my-vec [1] assoc:a"baz2":b"spam2")`似乎更清晰. (4认同)

Art*_*ldt 8

在地图矢量上映射函数,如果键匹配则创建修改后的映射,如果键不匹配则使用原始映射然后将结果转换回矢量

(vec (map #(if (= (:id %) 1) 
             (assoc % :a "baz2" :b "spam2")
             %)))
Run Code Online (Sandbox Code Playgroud)

有可能更简洁地做到这一点虽然这个确实显示了结构共享发生的位置.