使用specter进行递归映射查询

时间:2017-03-19 16:55:58

标签: clojure specter

在幽灵中是否有一种简单的方法来收集满足谓词的所有结构?

(./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"]

我们如何才能获得以下所有价值:名称,包括["镇流器" "赫尔"]?

3 个答案:

答案 0 :(得分:4)

这是一种方法,使用recursive-pathstay-then-continue来完成实际工作。 (如果从:name的路径参数中省略最后的select,您将获得完整的“项目/部分地图”,而不仅仅是: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"]

答案 1 :(得分:1)

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}]

答案 2 :(得分:1)

我认为您可以使用clojure.walk包递归地迭代地图。在每一步中,您可以检查谓词的当前值并将其推入原子以收集结果。