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?For posterity, I ended up pulling the subitems out and using (m/search ?subitems (m/scan ...) on them
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...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
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