Fork me on GitHub
#meander
<
2022-02-12
>
xiongtx00:02:16

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.

noprompt01:02:44

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.

noprompt01:02:41

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!).

👍 2
noprompt01:02:42

Still, I think its a fun exercise to write out the semantically equivalent rules of a particular group by as practice.

Richie02:02:35

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.

Richie02:02:21

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.

Richie16:02:57

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.

Richie16:02:59

(defn splat-fragments
    [xs]
    (cond
      (sequential? xs) (->> (map splat-fragments xs)
                            (map
                             #(if (and (sequential? %) (= :<> (first %)))
                                (rest %)
                                [%]))
                            (apply concat))
      :else xs))

noprompt23:02:20

(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]

noprompt23:02:12

Somewhere on one of my hard drives I had a generic rule for this kind of thing.

noprompt23:02:37

You can do a similar move to flatten operations like + or something.

Richie00:02:20

Thanks. I’m still digesting it.

noprompt00:02:37

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)

noprompt00:02:54

m/with and m/cata are what you want to reach for these kinds of situations. Think grammar. Think parser.

Richie00:02:25

I haven’t used ‘with’ successfully yet. This example helps me a lot though. Thanks!

noprompt00:02:16

m/with is conceptually like letfn if that helps at all.