This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-07-28
Channels
- # admin-announcements (4)
- # beginners (11)
- # boot (148)
- # cider (74)
- # cljs-dev (31)
- # cljsrn (30)
- # clojure (55)
- # clojure-berlin (15)
- # clojure-greece (1)
- # clojure-japan (18)
- # clojure-poland (35)
- # clojure-russia (72)
- # clojure-spec (35)
- # clojure-uk (34)
- # clojurescript (134)
- # cursive (26)
- # datomic (42)
- # dirac (7)
- # editors-rus (1)
- # emacs (17)
- # hoplon (29)
- # jobs-rus (3)
- # juxt (1)
- # luminus (11)
- # off-topic (9)
- # om (66)
- # onyx (49)
- # pedestal (1)
- # perun (19)
- # proton (13)
- # protorepl (5)
- # re-frame (31)
- # reagent (13)
- # ring (2)
- # spacemacs (1)
- # specter (40)
- # spirituality-ethics (2)
- # test-check (41)
- # untangled (7)
- # yada (17)
how do you do a recursive traversal? I want to create a transform for all of the handlers in a bidi routing structure. This requires getting all of the 'leaves' of a nested table/vector structure, where 'leaf' is basically a map-val that is a keyword
@shader: The easiest way is to use walker
, though sometimes you need to define a custom navigator.
Another option would be to use the zipper navigators, though I haven't played with those personally.
Could you give an example of what you're trying to do? I'm not familiar with bidi routing structures.
I'd like to get all all of :trials-list, :trial-details, etc., without getting the :id keywords in the vectors (those define url parameters, not handlers)
So if you were traversing that data structure, you'd want to get a vector [:trials-list :trial-details :records-list :record-details :authorized-trials :revoke :messages :delete-message]
?
well, specifically a view I can pass to transform to replace them with an actual handler function
should be able to do it with declarepath
and providepath
there we go
=> (declarepath DEEP-MAP-VALS)
=> (providepath DEEP-MAP-VALS (sp/if-path map? [sp/MAP-VALS DEEP-MAP-VALS] sp/STAY))
=> (select DEEP-MAP-VALS {:a {:b 2} :c {:d 3 :e {:f 4}} :g 5})
[2 3 4 5]
=> (transform DEEP-MAP-VALS inc {:a {:b 2} :c {:d 3 :e {:f 4}} :g 5})
{:a {:b 3}, :c {:d 4, :e {:f 5}}, :g 6}
dammit, keep hitting enter instead of ctrl + enter
@codonnell: thanks, that helped a lot. My final implementation is:
(providepath DEEP-MAP-VALS (cond-path map? [MAP-VALS DEEP-MAP-VALS] vector? [LAST DEEP-MAP-VALS] :default STAY))
@nathanmarz: I added DEEP-MAP-VALS
as an example to my copy of the wiki, since people ask about recursive navigators pretty often.
@codonnell: good idea, merged it in
I'm not sure I fully understand how to do a recursive transform. I want to change the value of the key :changeme
to negative its current value. This should be done for every nested map inside the structure, including all maps inside a sequential collection. It seems like this would be super simple to do in specter but I am guessing I don't fully understand the way recursion works in specter. Here is what I wrote:
(rsm/declarepath MapWalker)
(rsm/providepath MapWalker
(rs/cond-path
vector?
[rs/ALL MapWalker]
map?
[rs/MAP-VALS coll? MapWalker]))
(rsm/transform [MapWalker :changeme]
#(- %)
{:changeme 1
:ignore-this ""
:foo2 {:changeme 2
:bar2 "something"}
:foo3 [{:changeme 3}
{:changeme 3
:ignore-this ""}]})
Expected output:
{:changeme -1
:ignore-this ""
:foo2 {:changeme -2
:bar2 "something"}
:foo3 [{:changeme -3}
{:changeme -3
:ignore-this ""}]}
Any help?@kenny: easiest is to use walker
(declarepath MapWalker)
(providepath MapWalker
(stay-then-continue
MAP-VALS
(walker map?)
MapWalker
))
@kenny: you can modify it like this to make it work
(declarepath MapWalker)
(providepath MapWalker
(cond-path
vector?
[ALL MapWalker]
map?
(stay-then-continue MAP-VALS MapWalker)))
@kenny: I think the problem is you're not actually navigating to anything. You navigate deeper when you encounter a vector or a map, but you stop navigation when you encounter anything else, rather than staying there.
Indeed
Though as @nathanmarz neatly solved for your use case, you want to stay at each map, since you want to grab its value for :changeme
Agreed, I definitely like @nathanmarz solution much better.
In this case I guess the equivalent Clojure code wouldn't be all that bad to maintain anyways as I could have just used clojure.walk.
another thing to note is the second solution is significantly faster than using walker
or clojure.walk manually
It is interesting that each approach you take in Clojure has an "equivalent" approach in specter -- clojure.walk is to walker as manually writing a recursive function is to cond-path.
the nature of abstraction I think
like how every clojure function could be written manually in bytecode
Oh I think this declarepath feature is what I was looking for as well for recursive navigation...I notice that in case of unexpected nil sometimes on the path I am navigating, an exception is thrown...still need to investigate so I will come up with a better test case to submit here