使用幽灵的递归地图查询
Recursive map query using specter
在spectre中有没有一种简单的方法来收集满足谓词的所有结构?
(./pull '[com.rpl/specter "1.0.0"])
(use 'com.rpl.specter)
(def data {:items [{:name "Washing machine"
:subparts [{:name "Ballast" :weight 1}
{:name "Hull" :weight 2}]}]})
(reduce + (select [(walker :weight) :weight] data))
;=> 3
(select [(walker :name) :name] data)
;=> ["Washing machine"]
我们如何获取 :name 的所有值,包括 ["Ballast" "Hull"]?
selected?
选择器可用于收集另一个选择器与结构中的某些内容相匹配的结构
来自 https://github.com/nathanmarz/specter/wiki/List-of-Navigators#selected
中的示例
=> (select [ALL (selected? [(must :a) even?])] [{:a 0} {:a 1} {:a 2} {:a 3}])
[{:a 0} {:a 2}]
我认为您可以使用 clojure.walk
包递归迭代地图。在每一步中,您都可以检查谓词的当前值并将其推入原子以收集结果。
这是一种方法,使用 recursive-path
和 stay-then-continue
来完成真正的工作。 (如果您从 select
的路径参数中省略最后的 :name
,您将获得完整的“项目/零件图”,而不仅仅是 :name
字符串。)
(def data
{:items [{:name "Washing machine"
:subparts [{:name "Ballast" :weight 1}
{:name "Hull" :weight 2}]}]})
(specter/select
[(specter/recursive-path [] p
[(specter/walker :name) (specter/stay-then-continue [:subparts p])])
:name]
data)
;= ["Washing machine" "Ballast" "Hull"]
更新: 在回答下面的评论时,这里有一个版本的上面的下降到树的任意分支,而不是只下降到 :subparts
任何给定节点的分支,不包括 :name
(这是我们要提取的树中其值的键,它本身不应被视为分支点):
(specter/select
[(specter/recursive-path [] p
[(specter/walker :name)
(specter/stay-then-continue
[(specter/filterer #(not= :name (key %)))
(specter/walker :name)
p])])
:name]
;; adding the key `:subparts` with the value [{:name "Foo"}]
;; to the "Washing machine" map to exercise the new descent strategy
(assoc-in data [:items 0 :subparts2] [{:name "Foo"}]))
;= ["Washing machine" "Ballast" "Hull" "Foo"]
在spectre中有没有一种简单的方法来收集满足谓词的所有结构?
(./pull '[com.rpl/specter "1.0.0"])
(use 'com.rpl.specter)
(def data {:items [{:name "Washing machine"
:subparts [{:name "Ballast" :weight 1}
{:name "Hull" :weight 2}]}]})
(reduce + (select [(walker :weight) :weight] data))
;=> 3
(select [(walker :name) :name] data)
;=> ["Washing machine"]
我们如何获取 :name 的所有值,包括 ["Ballast" "Hull"]?
selected?
选择器可用于收集另一个选择器与结构中的某些内容相匹配的结构
来自 https://github.com/nathanmarz/specter/wiki/List-of-Navigators#selected
中的示例=> (select [ALL (selected? [(must :a) even?])] [{:a 0} {:a 1} {:a 2} {:a 3}])
[{:a 0} {:a 2}]
我认为您可以使用 clojure.walk
包递归迭代地图。在每一步中,您都可以检查谓词的当前值并将其推入原子以收集结果。
这是一种方法,使用 recursive-path
和 stay-then-continue
来完成真正的工作。 (如果您从 select
的路径参数中省略最后的 :name
,您将获得完整的“项目/零件图”,而不仅仅是 :name
字符串。)
(def data
{:items [{:name "Washing machine"
:subparts [{:name "Ballast" :weight 1}
{:name "Hull" :weight 2}]}]})
(specter/select
[(specter/recursive-path [] p
[(specter/walker :name) (specter/stay-then-continue [:subparts p])])
:name]
data)
;= ["Washing machine" "Ballast" "Hull"]
更新: 在回答下面的评论时,这里有一个版本的上面的下降到树的任意分支,而不是只下降到 :subparts
任何给定节点的分支,不包括 :name
(这是我们要提取的树中其值的键,它本身不应被视为分支点):
(specter/select
[(specter/recursive-path [] p
[(specter/walker :name)
(specter/stay-then-continue
[(specter/filterer #(not= :name (key %)))
(specter/walker :name)
p])])
:name]
;; adding the key `:subparts` with the value [{:name "Foo"}]
;; to the "Washing machine" map to exercise the new descent strategy
(assoc-in data [:items 0 :subparts2] [{:name "Foo"}]))
;= ["Washing machine" "Ballast" "Hull" "Foo"]