如何根据 Clojure 中的一系列值修改嵌套在向量中的映射?

THX*_*137 1 clojure

假设我有这样的数据结构:

[[{:name "bob" :favorite-color "green"}{:name "tim" :favorite-color "blue"}]
[{:name "eric" :favorite-color "orange"}{:name "jim" :favorite-color "purple"}]
[{:name "andy" :favorite-color "green"}{:name "tom" :favorite-color "blue"}]]
Run Code Online (Sandbox Code Playgroud)

和这样的数组:

["green" "purple"]
Run Code Online (Sandbox Code Playgroud)

我将如何传递我的数据结构并为喜欢我数组中颜色的人增加所有映射,并使用新的键值对 of :likes-my-colors "yes"

结果将是:

[[{:name "bob" :favorite-color "green" :likes-my-colors "yes"}{:name "tim" :favorite-color "blue"}]
 [{:name "eric" :favorite-color "orange"}{:name "jim" :favorite-color "purple" :likes-my-colors "yes"}]
 [{:name "andy" :favorite-color "green" :likes-my-colors "yes"}{:name "tom" :favorite-color "blue"}]]
Run Code Online (Sandbox Code Playgroud)

(我故意将值yes设为一串而不是true因为这更接近我想要弄清楚的)。

我尝试了循环和postwalk递归,但无法弄清楚如何通过后续递归来改变地图。我不会在这里粘贴我可怕的尝试,因为我猜有比 recur 更好的方法来做到这一点。但是,postwalk它的优势在于能够处理更多嵌套的数据结构,这很可能就是这种情况。所以也许 recur withpostwalk是要走的路。

我正在使用 ClojureScript 和 Reagent 将应用程序状态存储在一个原子中......随着事情的发生,我需要不断更新该原子中的应用程序状态。应用程序状态在单个用户会话中反复重置......每次重置后都会建立和修改它。在本例中,应用程序状态根据数组进行修改。我的代码需要遍历数组的元素并修改所有满足条件的映射。最终,该结构用于向 Hiccup 数据结构添加类。UI 会相应改变;如果列表中的人喜欢我的颜色,例如通过添加一个类,他们会在他们周围出现边框。

我在学习如何查看这样的数据结构并在给定特定键/值对的情况下更新所有映射方面获得了极大的帮助……但是我在使用一系列值时遇到了麻烦。换句话说,从某种意义上说,“建立”一张地图……但它更像是“通过多次修改”。这种措辞有望随着我的理解而改进。

作为旁注,我想知道 Clojure 用户如何访问和改变深埋在嵌套数据结构中的元素。我宁愿有更复杂的数据结构,但我避免使用它们,因为修改深层元素似乎很难。我怀疑他们可能会使用图书馆。与编写脑筋急转弯(对我而言)代码相比,似乎有一种更简单的方法来获取和修改复杂的结构。但话说回来,我可能错了。网上有很多例子,但它们通常是关于修改简单结构的。

lee*_*ski 5

我将从项目更新程序开始,例如:

(defn handle-fav-colors [color-set data]
    (if (color-set (:favorite-color data))
      (assoc data :likes-my-color "yes")
      data))
Run Code Online (Sandbox Code Playgroud)

那么您就可以随意更新您的数据。像映射:

user> (mapv (partial mapv (partial handle-fav-colors #{"green" "purple"})) data)

;;=> [[{:name "bob", :favorite-color "green", :likes-my-color "yes"}
;;     {:name "tim", :favorite-color "blue"}]
;;    [{:name "eric", :favorite-color "orange"}
;;     {:name "jim", :favorite-color "purple", :likes-my-color "yes"}]
;;    [{:name "andy", :favorite-color "green", :likes-my-color "yes"}
;;     {:name "tom", :favorite-color "blue"}]]
Run Code Online (Sandbox Code Playgroud)

我个人不建议为此使用步行者,因为这个结构有规律,而步行者无限期地深入,导致弄乱一些深层嵌套地图的非零可能性。经验法则(对我有用):当您知道需要操作的数据结构的确切级别时,您不应该使用可能在此级别之上或之下运行的工具。

此外,由于 clojure(以及一般的 FP)都是关于小型可组合和可重用的实用程序,因此您可以首先构建适当的通用函数,例如嵌套集合映射:

(defn mapv-deep [level f data]
  (if (pos? level)
    (mapv (partial mapv-deep (dec level) f) data)
    (mapv f data)))

user> (mapv-deep 0 inc [1 2 3])
;; [2 3 4]
user> (mapv-deep 1 inc [[1 2] [3 4]])
;; [[2 3] [4 5]]
user> (mapv-deep 2 inc [[[1 2] [3 4]] [[5 6] [7 8]]])
;; [[[2 3] [4 5]] [[6 7] [8 9]]]
Run Code Online (Sandbox Code Playgroud)

和条件模拟 assoc

(defn assoc-when [data pred k v]
  (if (pred data)
    (assoc data k v)
    data))

user> (assoc-when {:a 10 :b 20} #(-> % :a even?) :even-a? true)
;;=> {:a 10, :b 20, :even-a? true}

user> (assoc-when {:a 11 :b 20} #(-> % :a even?) :even-a? true)
;;=> {:a 11, :b 20}
Run Code Online (Sandbox Code Playgroud)

所以现在任务可以这样解决:

(defn handle-fav-colors [color-set data]
  (assoc-when data (comp color-set :favorite-color) :likes-my-color "yes"))

user> (mapv-deep 1 (partial handle-fav-colors #{"green" "purple"}) data)

;;=> [[{:name "bob", :favorite-color "green", :likes-my-color "yes"}
;;     {:name "tim", :favorite-color "blue"}]
;;    [{:name "eric", :favorite-color "orange"}
;;     {:name "jim", :favorite-color "purple", :likes-my-color "yes"}]
;;    [{:name "andy", :favorite-color "green", :likes-my-color "yes"}
;;     {:name "tom", :favorite-color "blue"}]]
Run Code Online (Sandbox Code Playgroud)