Recursive map query using specter

谁都会走 提交于 2019-12-04 05:27:54

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

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

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.

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