I’m trying to find a more natural way of destructuring maps. Basically, I want:
(require '[clojure.core.logic :as l)
(l/run* [q b d]
(l/featurec q {:a {:b b}
:d d})
<some other constraints>
(l/== q {:a {:b 1
:c "irrelevant"}
:d [1 2 3]
:e "whatever"}))
To return ([{:a {:b 1}, :d [1 2 3]} 1 [1 2 3]])
, i.e. q
should have only the form specified in the featurec
constraint. What’s the best way to do that?@xiongtx Sorry for the late reply. It is possible to return only the part of the map that matched but it requires some advanced use of the library. If you know what you’re querying for (which is usually half the battle) you could simply construct the data on the right side as so:
(m/find {:a {:b 1
:c "irrelevant"}
:d [1 2 3]
:e "whatever"}
{:a {:b ?b}
:d ?d}
[{:a {:b ?b} :d ?d}
{:b ?b}
?d])
;; =>
[{:a {:b 1}, :d [1 2 3]} {:b 1} [1 2 3]]
Been using meander
for value extraction in some of our tests & it’s been great! E.g.
🤢
(-> query
graphql-request
(get-in [:data :applications :edges])
first
(get-in [:node :app_accounts :edges])
first
:node
(select-keys [:account_name]))
to
😄
(meander/match (graphql-request query)
{:data
{:applications
{:edges
[{:node
{:app_accounts
{:edges
[{:node
{:account_name ?account-name}}]}}}]}}}
{:account_name ?account-name})
Great job!Sweet! 👍
you can't really
I take that back, you can
I am back to can't. basically the way to build structures via unification requires and exact match, and featurec does not require an exact match, and core.logic doesn't provide relational operations on maps
Hmm, so you’re saying unification can’t do a partial match?
right
@xiongtx Meander’s pattern matcher is suited for this task (among others):
(m/find {:a {:b 1
:c "irrelevant"}
:d [1 2 3]
:e "whatever"}
{:a {:b ?b}
:d ?d
:as ?q}
[?q ?b ?d])
;; =>
[{:a {:b 1, :c "irrelevant"}
:d [1 2 3]
:e "whatever"}
1
[1 2 3]]
Ah, I knew this had to be a solved problem! Thanks!
You can even query maps for multiple answers.
(m/search {:a {:b 1
:c "irrelevant"}
:d [1 2 3]
:e {:f "whatever"}}
{?k {:as ?v}}
[?k ?v])
;; =>
([:e {:f "whatever"}] [:a {:b 1, :c "irrelevant"}])
We’re located in the #meander channel for more on the topic of pattern matching/rewriting.
Its a work in progress but we can do some neat stuff if you have these kinds of problems 🙂
Actually, I see that the ?q
returned by meander
is still contains keys that aren’t specified in the matching map, i.e. :c
& :e
.
Here’s more what I had in mind, based on https://stackoverflow.com/a/40560433 (obviously not a comprehensive solution).
(defn pathwalk [f path e]
(let [e' (f path e)]
(cond
(map? e') (->> e'
(map (fn [[k x]] [k (pathwalk f (conj path k) x)]))
(into (empty e')))
(coll? e') (->> e'
(map-indexed (fn [i x] (pathwalk f (conj path i) x)))
(into (empty e')))
:else e')))
(defn match-only
[match m]
(let [bindings (atom {})
form (atom {})]
(pathwalk (fn [path v]
(when (symbol? v)
(let [x (get-in m path)]
(swap! form assoc-in path x)
(swap! bindings assoc v x)))
v)
[]
match)
[@form @bindings]))
(match-only '{:a {:b b}
:d d}
{:a {:b 1
:c "irrelevant"}
:d [1 2 3]
:e "whatever"})
;; => [{:a {:b 1}, :d [1 2 3]} {b 1, d [1 2 3]}]