Fork me on GitHub
#meander
<
2022-12-07
>
Tanner Emerson20:12:07

I am trying to grab a single list of free and paid item ids from this nested structure. I have tried multiple approaches with no luck. I will post my attempts in the thread. Any ideas on how to approach this problem?

{:games [{:id "foo"
          :items {:free [{:id "a"} {:id "b"}]
                  :paid [{:id "c"}]}}

         {:id "bar"
          :items {:free [{:id "d"} {:id "c"}]
                  :paid [{:id "f"}]}}

         {:id "baz"}]}

target => ["a" "b" "c" "d" "c" "f"] 
;; order does not matter
;; ideally no duplicates but not required

Tanner Emerson20:12:26

This attempt accumulates all free items but ignores paid items

(m/rewrites {:games [{:id "foo"
                      :items {:free [{:id "a"} {:id "b"}]
                              :paid [{:id "c"}]}}

                     {:id "bar"
                      :items {:free [{:id "d"} {:id "c"}]
                              :paid [{:id "f"}]}}

                     {:id "baz"}]}
  {:games (m/scan {:items {:free (m/scan {:id !item-ids})
                           :paid (m/scan {:id !item-ids})}})}
  !item-ids)

Tanner Emerson20:12:47

This attempt gave me an empty list

(m/rewrites {:games [{:id "foo"
                      :items {:free [{:id "a"} {:id "b"}]
                              :paid [{:id "c"}]}}

                     {:id "bar"
                      :items {:free [{:id "d"} {:id "c"}]
                              :paid [{:id "f"}]}}

                     {:id "baz"}]}
  {:games [{:items {:free [{:id !free-ids}
                           ...]
                    :paid [{:id !paid-ids}
                           ...]}}
           ...]}
  [!free-ids ... . !paid-ids ...])

grant20:12:00

In an effort to learn Meander I am attempting to port some existing code to use it. Some of the examples have been pretty easy, but I’m struggling to figure out an elegant way, with Meander, to take input and produce target-out. Any suggestions or advice would be greatly appreciated.

(def input {:required  [{:id :e1 :a 1}
                          {:id :e2 :a 2}
                          {:id :e3 :a 3}
                          {:id :e4 :a 4}]
              :optional1 [{:id :e1 :b 1}
                          {:id :e2 :b 2}]
              :optional2 [{:id :e1 :c 1}
                          {:id :e3 :c 3}]})
(def target-out '({:id :e1 :a 1 :b 1 :c 1}
                    {:id :e2 :a 2 :b 2     }
                    {:id :e3 :a 3      :c 3}
                    {:id :e4 :a 4          }))

gdubs22:12:43

I've had this same question too. Its like you want to do an outer join instead of an inner join.

noprompt06:12:43

Yep. The other thing here is that m/or on epsilon finds all of the solutions which is why the solution set we get with m/rewrites has more results than we want. We can get around this by querying the required portion of the map with m/search and then extracting the optional stuff with m/find.

(m/search input
  {:required (m/scan {:id ?id, :as ?attrs})
   :optional1 ?optional1
   :optional2 ?optional2}
  (m/find [?id ?optional1 ?optional2]
    [?id
     (m/or (m/scan {:id ?id, & !attrs}) _)
     (m/or (m/scan {:id ?id, & !attrs}) _)]
    (reduce merge ?attrs !attrs)))
;; =>
({:id :e1, :a 1, :b 1, :c 1}
 {:id :e2, :a 2, :b 2}
 {:id :e3, :a 3, :c 3}
 {:id :e4, :a 4})

noprompt06:12:14

This would actually work, however, if epsilon had a short circuiting m/or.

noprompt06:12:24

Without the m/find.

noprompt06:12:30

This is what the rule currently looks like on zeta that finds the solution.

(m/rule
 {:required (m/scan {:id ?x & ?a}),
  :optional1 ?optional1,
  :optional2 ?optional2}
 (m/let [(m/pick (m/scan {:id ?x & ?b}) _) ?optional1
         (m/pick (m/scan {:id ?x & ?c}) _) ?optional2]
   {:id ?x
    &0 ?a
    &1 (m/pick ?b {})
    &2 (m/pick ?c {})}))

noprompt06:12:26

zeta has a short circuiting m/or called m/pick.

noprompt06:12:02

It is also slightly different in that everything that you can do on the left you can do on the right which is why m/let is there an there are guards around the variables ?b and ?c which may not be bound.

noprompt06:12:06

It might be possible add something like m/or! to epsilon

👍 1
grant18:12:37

Thanks you @U06MDAPTP and @UG9CG9GDB. Both of these are much clearer than the attempts I had managed so far. At this point, is Epsilon still recommended over Zeta, or should I start using Zeta since I don’t have any legacy code (or knowledge) to worry about?