meander

sergey 2022-07-27T16:49:44.149919Z

let's say I want to collect lists of matching patterns and also a map key at a higher level in a nested map. ie. given the following map:

{:a {:client "foo"
     :items [{:x 1} 2 3]}
 :b {:client "bar"
     :items [{:x "q"} {:x "x"}]}}
I'd like to collect all values of maps with key :x under :items and also the value of :client that appears alongside those :items:
[["foo" [1] ["bar" ["q" "x"]]
What's a good way of doing this? Is it possible to generate that output with meander alone?

sergey 2022-07-27T16:49:56.355989Z

I can get pretty close with the following:

(m/search {:a {:client "foo"
                 :items [{:x 1} 2 3]}
             :b {:client "bar"
                 :items [{:x "q"} {:x "x"}]}
             }
            (m/$ {:client ?c
                  :items (m/scan {:x !i})})
            [?c !i])
but it gives me pairs for each match rather than aggregating the !i values:
(["foo" [1]] ["bar" ["q"]] ["bar" ["x"]])

sergey 2022-07-27T16:52:05.843019Z

(For more context, the map above is a simplified one; the actual map I want to extract the data from has a lot more levels of nesting under :items before the :x keys appear)

noprompt 2022-07-28T18:23:37.568049Z

(defn f [m]
  (m/find m
    {:client ?client
     :items [(m/or {:x !x} _) ...]}
    [?client !x]))

(m/search {:a {:client "bar"
               :items [{:x "q"} {:x "x"}]}
           :b {:client "foo"
               :items [{:x 1} 2 3]}}
  {_ {:as ?v}} (f ?v))
;; =>
(["bar" ["q" "x"]] ["foo" [1]])

noprompt 2022-07-28T18:25:34.799399Z

This would all be one pattern if epsilon had proper greediness.

sergey 2022-07-29T18:38:08.936559Z

Gotcha - thanks for the context! I'll keep this in mind when I write queries in the future. The output without proper greediness is still very useful to me because it simplifies a lot of the map traversal logic that I'd otherwise have to write