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 requiredThis 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)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 ...])
How about this?
I don't have an explanation for why the paid ids are being duplicated.
If I switch the order of the m/or arguments, I get different duplicates. That surprises me.
This works, and is general.
(ns solution
(:require [clojure.test :refer [is]]
[meander.epsilon :as m]))
(def example
{: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"}]})
(defn transform
{:test #(is (transform example) ["a" "b" "c" "d" "c" "f"])}
[input]
(vec (m/search input
{:games (m/scan {:items {(m/or :free :paid)
(m/scan {:id ?x})}})}
?x)))
You can also do this without searching. I threw in the distinct there, but not strictly needed from the requirements.
(m/rewrite data
{:games [(m/or {:items {:free [{:id !free} ...]
:paid [{:id !paid} ...]}}
_) ...]}
(m/app distinct [!free ... !paid ...]))Thank you @jimmy for your 2019 meander strange loop talk. That's what got us here!
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 }))I've had this same question too. Its like you want to do an outer join instead of an inner join.
This is really close
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})
This would actually work, however, if epsilon had a short circuiting m/or.
Without the m/find.
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 {})}))zeta has a short circuiting m/or called m/pick.
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.
It might be possible add something like m/or! to epsilon