更新矢量内地图的clojure方法是什么?如果我有这样的东西,假设每张地图都有唯一的:name
(def some-vec
[{:name "foo"
....}
{:name "bar"
....}
{:name "baz"
....}])
如果:name
等于foo
,我想以某种方式更新地图。目前我正在使用map
,就像这样
(map (fn [{:keys [name] :as value}]
(if-not (= name "foo")
value
(do-something .....))) some-vec)
但即使我只更新了一个项目,这也会遍历整个矢量。
答案 0 :(得分:7)
将数据保存为地图,而不是:name
键入的地图记录矢量。
(def some-data
{"foo" {:name "foo" :other :stuff}
"bar" {:name "bar" :other :stuff}
"baz" {:name "baz" :other :stuff}})
然后
(assoc-in some-data ["bar" :other] :things)
产生
{"foo" {:other :stuff, :name "foo"},
"bar" {:other :things, :name "bar"},
"baz" {:other :stuff, :name "baz"}}
一气呵成。
您可以在
中捕获基本操作(defn assoc-by-fn [data keyfn datum]
(assoc data (keyfn datum) datum))
例如,
(assoc-by-fn some-data :name {:name "zip" :other :fassner})
产生
{"zip" {:other :fassner, :name "zip"},
"foo" {:other :stuff, :name "foo"},
"bar" {:other :stuff, :name "bar"},
"baz" {:other :stuff, :name "baz"}}
答案 1 :(得分:1)
鉴于你有一张地图矢量,你的代码对我来说很好。您关注"循环遍历整个矢量"是你对:name
进行线性搜索以及向量是不可变的这一事实的自然结果。
我想知道你真正想要的是地图矢量吗?为什么不是地图地图?
(def some-map
{"foo" {...}
"bar" (...}
"baz" {...}}
然后您可以使用update-in
更新哪些内容?
答案 2 :(得分:1)
考虑到输入数据的这种形状,除非你有一个索引可以告诉你具有给定值:name
的地图所在的索引,你将不得不循环遍历整个矢量。但是,您可以通过仅更新"来最小化生成更新向量所涉及的工作量。匹配的地图,而不是重建整个矢量:
(defn update-values-if
"Assumes xs is a vector. Will update the values for which
pred returns true."
[xs pred f]
(let [lim (count xs)]
(loop [xs xs i 0]
(if (< i lim)
(let [x (nth xs i)]
(recur (if (pred x)
(assoc xs i (f x))
xs)
(inc i)))
xs))))
这将执行尽可能多的assoc
次操作,因为xs
中的值pred
会返回真值。
示例:
(def some-vec [{:name "foo" :x 0} {:name "bar" :x 0} {:name "baz" :x 0}])
(update-values-if some-vec #(= "foo" (:name %)) #(update-in % [:x] inc))
;= [{:name "foo", :x 1} {:name "bar", :x 0} {:name "baz", :x 0}]
当然,如果您计划以这种方式有规律地转换矢量,那么缩略图和保罗建议使用地图将是一个更为显着的改进。如果:name
没有唯一标识地图,情况仍然如此 - 在这种情况下,您可以使用frequencies
简单地转换原始矢量并处理矢量地图(具有给定地图的地图) :name
)。
答案 3 :(得分:0)
如果您正在使用vector,您应该知道要更改的元素的索引,否则您必须以某种方式遍历它。
我可以提出这个解决方案:
(defn my-update [coll val fnc & args]
(let [index (->> (map-indexed vector coll)
(filter (fn [[_ {x :name}]] (= x val)))
ffirst)]
(when index
(apply update-in coll [index] fnc args))))
其中:
coll
- 给出地图集;
val
- 字段:name
的值;
fnc
- 更新功能;
args
- 更新函数的参数。
我们试一试:
user> (def some-vec
[{:name "foo"}
{:name "bar"}
{:name "baz"}])
;; => #'user/some-vec
user> (my-update some-vec "foo" assoc :boo 12)
;; => [{:name "foo", :boo 12} {:name "bar"} {:name "baz"}]
user> (my-update some-vec "bar" assoc :wow "wow!")
;; => [{:name "foo"} {:name "bar", :wow "wow!"} {:name "baz"}]
我认为Thumbnail's answer可能对您非常有用。如果您可以将数据保存为地图,则这些操作变得更加容易。以下是将矢量转换为地图的方法:
user> (apply hash-map (interleave (map :name some-vec) some-vec))
;; => {"foo" {:name "foo"}, "bar" {:name "bar"}, "baz" {:name "baz"}}