meander

Max 2026-01-07T21:07:06.672199Z

Bit of a puzzle for y'all: I'm starting with a data structure that looks kinda like this:

{:items
 [{:id 1
   :price 34.50M
   :subitems
   [{:parts
     [{:id 2} ...]}]}
  ...]}
but with a bunch more data in it that I want to match out of it. One of the things I'd like to get is the average price per part, i.e. (sum of (price * # parts per item)) / total # parts. I'm using m/match and for various reasons can't switch to m/search or m/rewrite. The pattern I have so far looks like:
{:items [{:subitems [{:parts [!part ...]}]} ...]}
For the total number of parts I can just do (count !parts), but for the other bits I need some way of getting a match against the prices and parts qualified by item, perhaps something like [{:price (m/some), :parts ?}] that I could then compute against. I could always match all the items into a memory variable and then match against each one separately, but I'm already plumbing down to the parts for !parts anyways, so I was hoping there'd be a way to do it without so much repetition. Is there a better way to do it?

Max 2026-01-09T16:03:38.761929Z

For posterity, I ended up pulling the subitems out and using (m/search ?subitems (m/scan ...) on them

Max 2026-01-08T15:53:41.402349Z

This approach seems close to working:

(m/match {:x [{:a 1 :b {:c 3}} {:a 2 :b {:c 5}}]}
    {:x [(m/and {:a *a
                 :b {:c *c}}
                (m/let [!r [*a *c]]))
         ...]}
    !r)
;; => [[1 3] [2 5]]
But if one of the things you want to extract is in a nested sequence, it doesn't work:
(m/match {:x [{:a 1 :b {:cs [{:d 4} {:d 5}]}} 
                {:a 2 :b {:cs [{:d 5} {:d 6}]}}]}
    {:x [(m/and {:a *a
                 :b {:cs [{:d *d} ...]}}
                (m/let [!r [*a *d]]))
         ...]}
    !r)
;; => Unable to resolve symbol: *d in this context
Using a memory variable for d accumulates across matches, which isn't right
(m/match {:x [{:a 1 :b {:cs [{:d 4} {:d 5}]}} 
                {:a 2 :b {:cs [{:d 5} {:d 6}]}}]}
    {:x [(m/and {:a *a
                 :b {:cs [{:d !d} ...]}}
                (m/let [!r [*a !d]]))
         ...]}
    !r)
;; => [[1 [4 5]] [2 [4 5 5 6]]]
So I'm back to needing some sort of scoped memory variables...

Max 2026-01-07T21:26:57.845669Z

It kinda feels like what I want some kind of sub-match scope that can have memory variables whose values start fresh for each instance of the scope

Max 2026-01-07T21:33:02.868519Z

Note that in the real thing there's even another layer of nesting between subitems and parts, so it's really convenient to be able to use a memory variable to aggregate them rather than just counting the list