Wondering how I can do the following w/out a group-by:
(let [coll [{:foo "a" :bar "b" :baz 123}
{:foo "x" :bar "y" :baz 666}
{:foo "a" :bar "b" :baz 987}]]
(m/search (group-by (juxt :foo :bar) coll)
(m/scan [[?foo ?bar] [{:baz !baz} ...]])
{:foo ?foo
:bar ?bar
:children (for [v !baz]
{:baz v})}))
;; =>
({:foo "a", :bar "b", :children ({:baz 123} {:baz 987})}
{:foo "x", :bar "y", :children ({:baz 666})})
I figure it’d need some way of remembering the different foo & bar values and baz associated w/ them…keep in mind that the maps w/ same foo/`bar` pairs aren’t necessarily in order.This comes up from time to time (group-by) which currently has no clean solution but will (hopefully) later this year. For these situations group-by is what I typically recommend because the alternative is not always that great.
(let [coll [{:foo "a" :bar "b" :baz 123}
{:foo "x" :bar "y" :baz 666}
{:foo "a" :bar "b" :baz 987}]]
(m/rewrite coll
(m/with [%it {:foo ?foo, :bar ?bar, :baz !baz}]
[%it . (m/or %it !not-it) ...])
({:foo ?foo, :bar ?bar, :children [!baz ...]}
& (m/cata [!not-it ...]))
_
()))
Here the issue is performance due to nodes being visited again and again. We can try a different approach.
(let [coll [{:foo "a" :bar "b" :baz 123}
{:foo "x" :bar "y" :baz 666}
{:foo "a" :bar "b" :baz 987}]]
(m/rewrite [coll {}]
[[{:foo ?foo, :bar ?bar, :baz ?baz} & ?rest]
(m/and ?index
(m/or {[?foo ?bar] ?children}
(m/let [?children []])))]
(m/cata [?rest {[?foo ?bar] [?baz & ?children] & ?index}])
[[] (m/map-of [!foo !bar] !children)]
({:foo !foo, :bar !bar, :children !children} ...)))
This approach is, eh, not terribly attractive but I think it should be more performant than the other.Either way, I think group-by is much easier to wield and has more utility than what Meander can offer at the moment (which is okay!).
Still, I think its a fun exercise to write out the semantically equivalent rules of a particular group by as practice.
Hey. This seems simple enough but it’s been hours (I don’t want to admit how many) and I haven’t got a solution. I want to turn this
[[:<>]
'four
[:<> 'one ['three [:<> 'two]]]
[:<> [:<> 'three]]]
into this
['four 'one ['three 'two] 'three]
I’m trying to splat any vector that starts with that keyword into the parent vector.This comes up because I have multiple things on the rhs of my rewrite rule but I don’t want it in a vector in the parent vector. e.g.
{:node-type :module
:body [(m/app f !body) ...]}
[:<> (ns file-name-maybe)
& [!body ...]]
{:node-type :name
:id (m/app keb-sym ?id)}
?id
The best idea I’ve had so far is to denote the ones that I want to flatten and flatten them after.Oh. This is interesting. I kept trying things that worked top down but then I’d skip collapsing a level. I needed something that worked botom up.
(defn splat-fragments
[xs]
(cond
(sequential? xs) (->> (map splat-fragments xs)
(map
#(if (and (sequential? %) (= :<> (first %)))
(rest %)
[%]))
(apply concat))
:else xs))
(let [input [[:<>]
'four
[:<> 'one ['three [:<> 'two]]]
[:<> [:<> 'three]]]]
(m/rewrite input
(m/with [%splice (m/or [:<> . (m/or %splice %node) ...] %node)
%node (m/cata !node)]
[%splice ...])
[!node ...]
?x ?x))
;; =>
[four one [three two] three]
Somewhere on one of my hard drives I had a generic rule for this kind of thing.
Woa.
You can do a similar move to flatten operations like + or something.
Thanks. I’m still digesting it.
This might be a tad clearer?
(m/rewrite input
(m/with [%splice [:<> . %main ...]
%node (m/cata !node)
%main (m/or %splice %node)]
[%main ...])
[!node ...]
?x ?x)
m/with and m/cata are what you want to reach for these kinds of situations. Think grammar. Think parser.
I haven’t used ‘with’ successfully yet. This example helps me a lot though. Thanks!
m/with is conceptually like letfn if that helps at all.