我如何在 Clojure 中的一系列向量中查找然后更新地图中的值?

How do I find then update values in maps in a series of vectors in Clojure?

在如下数据结构中查找 :fooba4 值的优雅方法是什么:

[[{:foo "ba1"}{:foo "ba2"}] [{:foo "ba3"}{:foo "ba4"}]]

并添加 :bif "baf" 键和值,这样包含我寻找的 key/value 的地图就得到了:

[[{:foo "ba1"}{:foo "ba2"}] [{:foo "ba3"}{:foo "ba4" :bif "baf"}]]?

这是我想弄清楚的主要问题;我确实想知道如果有多层嵌套地图你会怎么做,但这将是我接下来要理解的。

我认为,我看到过各种用于处理此类问题的库,我一点也不反对使用它们,尽管它们必须在 ClojureScript 中工作。

我正在尝试更新试剂原子,以便 类 将在 UI 元素上发生变化。

Clojure 可以很方便地为这类事情定义迷你语言。通用且相当可重用的解决方案不必比专用解决方案长很多。如果您对使用图书馆不感兴趣,这里有一个建议。

本质上,您需要一个函数来更新数据结构。这个函数可以使用作为构建块的同类基本函数来表达,构成我之前提到的迷你语言。在此示例中,构建块将是 vector-scannermap-decorator。我们将这些函数组合成 my-path 来执行完整更新。

;; Path building blocks
(defn vector-scanner [subpath]
  #(mapv subpath %))

(defn map-decorator [k v decoration]
  #(if (and (map? %) (= v (k %)))
     (merge % decoration)
     %))

;; Function to do the update
(def my-path (-> (map-decorator :foo "ba4" {:bif "baf"})
                 vector-scanner
                 vector-scanner))

;; Running it
(my-path [[{:foo "ba1"}{:foo "ba2"}] [{:foo "ba3"}{:foo "ba4"}]])
;; => [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4", :bif "baf"}]]

这种方法在本质上与 transducers 可以在各种配置中组合的方式非常相似。

(我学到了一点 decorators and merge from 。)

您所描述的是嵌套数据结构的 walking a nested data structure. Postwalking or prewalking take every possible subform 并对其应用函数。

以下是我如何使用 postwalk (note: assoc 向地图添加键值来获得所需的解决方案):

(ns walking.core
  (:require [clojure.walk :as w]))

(clojure.walk/postwalk #(if (and (map? %) (= (:foo %) "ba4")) (assoc % :bif "baf") %) 
  [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4"}]])
;;=> [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4", :bif "baf"}]]

无论嵌套如何,它都能正常工作:

(clojure.walk/postwalk #(if (and (map? %) (= (:foo %) "ba4")) (assoc % :bif "baf") %) 
  [[{:foo "ba1"} {:foo "ba2"}] [[{:foo "ba3"} {:test {:foo "ba4"}}]]])
;;=> [[{:foo "ba1"} {:foo "ba2"}] [[{:foo "ba3"} {:test {:foo "ba4", :bif "baf"}}]]]

为什么 postwalk 而不是 prewalk?两者都有效。

(clojure.walk/prewalk #(if (and (map? %) (= (:foo %) "ba4")) (assoc % :bif "baf") %) 
  [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4"}]])
;;=> [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4", :bif "baf"}]]

您不必 lein install 任何东西,它是 Clojure 的一部分;你只需要要求它。