specter

2024-08-22T14:06:50.691659Z

Question for the specter experts, I have a data structure like

{:roots {:query :Query},
 :objects
 {:Query
  {:fields
   {:ping {:type (non-null :Status)},
    :getList {:type (list (non-null String))},
    :getListError {:type (list (non-null String))},
    :introspection {:type String},
    :error {:type String},
    :shouldBeHidden
    {:type (non-null String),
     :directives [{:directive-type :hidden}]}}},
  :Status {:fields {:pong {:type (non-null String)}}},
  :ShouldIgnore
  {:fields {:f1 {:type String}},
   :directives [{:directive-type :hidden}]}}}
And I want to filter out all the keys/values where the value contains {:directives {:directive-type :hidden}} So after filtering that map should become
{:roots {:query :Query},
 :objects
 {:Query
  {:fields
   {:ping {:type (non-null :Status)},
    :getList {:type (list (non-null String))},
    :getListError {:type (list (non-null String))},
    :introspection {:type String},
    :error {:type String},}},
  :Status {:fields {:pong {:type (non-null String)}}}}} 
I got it working with a fixed depth, but the thing to hide can be either on 3rd or 4th level of nesting, I can't understand how to make that work

2024-08-22T14:07:12.680439Z

I've done something similar to

(defn non-hidden-leaf?
  [v]
  (when-not (-> v
                :directives
                set
                (contains? {:directive-type :hidden}))
    v))

(defn filter-hidden
  [v]
  (->> v
       (sp/transform [sp/MAP-VALS] non-hidden-leaf?)
       (sp/transform [sp/ALL] #(if (nil? %) sp/NONE %))))

rolt 2024-08-22T14:24:03.920059Z

you can use MAP-NODES from the wiki: https://github.com/redplanetlabs/specter/wiki/Using-Specter-Recursively#recursively-navigate-to-every-map-in-a-map-of-maps (sp/transform [MAP-NODES sp/MAP-VALS] non-hidden-leaf?)

rolt 2024-08-22T14:25:05.173339Z

also why not use NONE directly in non-hidden-leaf? ?

rolt 2024-08-22T14:25:19.362469Z

(defn non-hidden-leaf?
  [v]
  (if (-> v
                :directives
                set
                (contains? {:directive-type :hidden}))
    sp/NONE
    v))

2024-08-22T14:29:39.340229Z

ah yeah I think I tried to use NONE directly but didn't work

2024-08-22T14:29:41.802049Z

I can try again

rolt 2024-08-22T14:42:00.427109Z

let me know if it worked

rolt 2024-08-22T14:43:17.223959Z

unrelated: clojure convention is that functions ending with ? return a boolean value

2024-08-22T15:56:38.006689Z

ah yeah it was returning a boolean, I haven't updated it after I changed it

nathanmarz 2024-08-22T17:57:23.410869Z

does this require a recursive transformation, or are you always looking at the top-level fields of each item under :objects?

2024-08-22T17:58:29.818479Z

The thing to hide could be either in objects or in fields

2024-08-22T17:58:36.363049Z

So two different depths

nathanmarz 2024-08-22T18:54:56.528609Z

in that case, I wouldn't use a recursive path but would explicitly target what you want to remove

nathanmarz 2024-08-22T18:55:00.283259Z

(setval [:objects
         MAP-VALS
         (multi-path [:fields MAP-VALS]
                     STAY)
         map?
         (selected?
           :directives
           ALL
           :directive-type
           (pred= :hidden))]
  NONE
  data)

2024-08-23T09:15:40.374739Z

ah brilliant that works