This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-16
Channels
- # aleph (1)
- # announcements (3)
- # babashka (24)
- # beginners (333)
- # clj-kondo (36)
- # cljs-dev (11)
- # clojure (75)
- # clojure-italy (3)
- # clojure-uk (15)
- # clojurescript (31)
- # core-logic (18)
- # cursive (2)
- # data-science (3)
- # datomic (1)
- # events (1)
- # fulcro (13)
- # graalvm (2)
- # jobs (1)
- # kaocha (2)
- # malli (1)
- # overtone (6)
- # re-frame (7)
- # reagent (17)
- # rewrite-clj (3)
- # shadow-cljs (10)
- # sql (9)
- # vim (1)
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?@U2J7JRTDX 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]]
(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]}]
@U2J7JRTDX 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!