如何更新 Clojure 地图嵌套向量中的试剂原子过滤

How to update a reagent atom filtering in a nested vector of maps in Clojure

假设我有一个试剂原子,其映射向量如下:

(def my-atom (reagent/atom {:id 256 
                             :name "some name"
                             :lines [{:code "ab43" :name "first nested name" :quantity 4}
                                     {:code "bc22" :name "second nested name" :quantity 1}
                                     {:code "lu32" :name "third nested name" :quantity 1}}] }))

如何更新键的值:某个向量嵌套索引处的数量,例如:将代码为“bc22”的行更新为 10 个数量。

这需要过滤以获取向量的索引,但没有索引,因为按“代码”过滤:

 (swap! my-atom assoc-in [:lines 1 :quantity] 10)

我可以用过滤器找到,但我不能交换!数量:

(->> (:lines @my-atom)
     (filter #(= (:code %) "bc22")
     first))
(require
    '[com.rpl.specter :as s])

(let [*a (atom {:id    256
                   :name  "some name"
                   :lines [{:code "ab43" :name "first nested name" :quantity 4}
                           {:code "bc22" :name "second nested name" :quantity 1}
                           {:code "lu32" :name "third nested name" :quantity 1}]})]
        (s/setval [s/ATOM :lines s/ALL #(-> % :code (= "bc22")) :quantity] 10 *a))

您可以坚持使用 assoc-in,但要这样做,您必须以某种方式从 :lines 字段的向量中检索与给定代码关联的索引。

例如,我要一个辅助函数:

(defn code->index [data code]
  (->> data
       :lines
       (map-indexed (fn [i v] [i v]))
       (filter (fn [[_ v]] (= (:code v) code)))
       ffirst))
;; (code->index @my-atom "bc22")
;; => 1

然后在交换中使用它:

(swap! my-atom assoc-in [:lines (code->index @my-atom "bc22") :quantity] 10)

这里有选项。您可以查找项目的索引,可以映射列表,只更新您感兴趣的项目。

根据具体情况,您还可以考虑在呈现组件时存储元素的索引,或者构建一组 cursors 传递给您的组件。在那种情况下,您只需像更新原子一样更新游标,它会有效地处理更新支持原子。

就我个人而言,我看了这个并想知道您是否首先使用了正确的数据结构。 code 似乎很可能是这里的自然键,尤其是当您希望基于它更新一行时。也许以 code 为键、以整行为值的映射更有意义。这也使得某些不良情况不可能发生(e.x。多行具有相同的code)。当然,您会丢失订单(除非您以某种方式 re-established 它),这可能是也可能不是问题。