如何在Clojure中更新向量项?

12

给定:

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

如何用成语更新my-vec中id为:id=1的项目,并使其具有值:a="baz2":b="spam2"

*:我意识到实际上我不会更新my-vec,而是返回一个与my-vec相同但替换值的新向量。

3个回答

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

如果你需要经常通过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"}}

嗯,我将它存储在向量中,因为实际上我所持有的是扑克牌的ImageIcons,而向量位置最终会在绘制时充当z-order。 [{:id "8H" :image ImageIcon} ...]。但是我可以尝试将它们存储在一个映射中{{:id "8H" :z-order 2 :image ImageIcon}...},然后通过按其排序的:z-order将它们拉出来进行Graphics.draw。谢谢,Brian,你是Clojure怪物。 :) - scrotty
4
(update-in my-vec [1] assoc :a "baz2" :b "spam2") 看起来更干净。 - amalloy

9

对映射向量进行函数映射,如果键匹配,则创建修改后的映射,或者使用原始映射(如果键不匹配),然后将结果转换回向量。

(vec (map #(if (= (:id %) 1) 
             (assoc % :a "baz2" :b "spam2")
             %)))

虽然可以更简洁地完成此操作,但这个方法确实展示了结构共享发生的位置。


3

你可能想看一下array-map,它创建了一个由数组支持且以索引为键的映射,而不是使用:id。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接