Recursive map query using specter

拟墨画扇 提交于 2019-12-21 13:04:21

问题


Is there a simple way in specter to collect all the structure satisfying a predicate ?

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

How can we get all the value for :name, including ["Ballast" "Hull"] ?


回答1:


Here's one way, using recursive-path and stay-then-continue to do the real work. (If you omit the final :name from the path argument to select, you'll get the full “item / part maps” rather than just the :name strings.)

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

Update: In answer to the comment below, here's a version of the above the descends into arbitrary branches of the tree, as opposed to only descending into the :subparts branch of any given node, excluding :name (which is the key whose values in the tree we want to extract and should not itself be viewed as a branching off point):

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



回答2:


The selected? selector can be used to collect structures for which another selector matches something within the structure

From the examples at 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}]



回答3:


I think you could iterate on map recursively using clojure.walk package. On each step, you may check the current value for a predicate and push it into an atom to collect the result.



来源:https://stackoverflow.com/questions/42889373/recursive-map-query-using-specter

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!