我如何在 Clojure 中的一系列向量中查找然后更新地图中的值?
How do I find then update values in maps in a series of vectors in Clojure?
在如下数据结构中查找 :foo
的 ba4
值的优雅方法是什么:
[[{: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-scanner
和 map-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 的一部分;你只需要要求它。
在如下数据结构中查找 :foo
的 ba4
值的优雅方法是什么:
[[{: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-scanner
和 map-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 的一部分;你只需要要求它。