This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-06-15
Channels
- # admin-announcements (7)
- # alda (1)
- # aws-lambda (1)
- # beginners (12)
- # boot (20)
- # cider (59)
- # cljs-dev (4)
- # cljsrn (69)
- # clojure (232)
- # clojure-austin (3)
- # clojure-austria (1)
- # clojure-belgium (2)
- # clojure-canada (3)
- # clojure-dev (16)
- # clojure-greece (33)
- # clojure-nl (4)
- # clojure-quebec (12)
- # clojure-russia (12)
- # clojure-spec (27)
- # clojure-uk (38)
- # clojurescript (29)
- # community-development (7)
- # component (53)
- # core-async (16)
- # core-logic (1)
- # datascript (7)
- # datomic (11)
- # editors (7)
- # emacs (69)
- # hoplon (157)
- # keechma (1)
- # lambdaisland (2)
- # lein-figwheel (31)
- # leiningen (8)
- # mount (3)
- # off-topic (11)
- # om (23)
- # onyx (64)
- # planck (2)
- # re-frame (18)
- # reagent (21)
- # specter (118)
- # untangled (145)
- # yada (1)
Putting up another puzzle, this one relating to filters or conditionals
Given a vector of maps
If the values of a key in the map is
a) A map that has a key of tempid
return just the tempid
if the value of a key is a set, list, or vector that contains maps with key of tempid
then replace each of those maps with their temp id
example
[{:a "foo",
:b "bar",
:c
[{:this "other thing",
:that
{:nested :map, :deeper #{{:in :we-go, :tempid -1}}, :tempid -2},
:tempid -3}],
:tempid -4}
{:this "other thing",
:that {:nested :map, :deeper #{{:in :we-go, :tempid -1}}, :tempid -2},
:tempid -3}
{:nested :map, :deeper #{{:in :we-go, :tempid -1}}, :tempid -2}
{:in :we-go, :tempid -1}]
would become
[{:a “foo” :b “bar” :c [-3] :tempid -4}
{:this “other thing” :that -2 :tempid -3}
{:nested :map :deeper #{-1} :tempid -2}
{:in :we-go :tempid -1}]
right now I have this, for the simpler case of just nested maps
(transform [ALL MAP-VALS (sp/pred :tempid) (collect-one :tempid)]
(fn [v _] v))
what would that transform to?
the transform I have works fine, but doesn’t handle the case when I have a map inside a seq
basically I want to find a way to conditionally insert and ALL between MAP-VALS and (pred :tempid)
i don't understand
you said when a map has a :tempid to replace it with the :tempid value
that would make that example transform to [-4 -3 -2 -1]
@conaw: or does that only happen after the first level of maps?
(transform [ALL MAP-VALS] #((if (and (map? %) #(:tempid %))) (:tempid %) (if (seq %) (keep :tempid %) %)) sampmap2)
got it
easy enough
(transform [ALL MAP-VALS ConawWalker (pred :tempid)] :tempid data)
I'll let you figure out ConawWalker
the solution I have uses cond-path
and it's recursive obviously
yea you do
but I’ve already flattened the tree out so that I only want to do my replacing at one level of depth in
not in that example
:tempid all over the place
the example came about this way
(def sampmap2
[{:a "foo"
:b "bar"
:c [{:this "other thing"
:that {:nested :map
:deeper [{:in :we-go}]}}]}])
(declarepath TOPSORT3)
(providepath TOPSORT3
(sp/cond-path
map?
(stay-then-continue
[MAP-VALS TOPSORT3])
vector?
[ALL TOPSORT3]))
(select TOPSORT3 sampmap2)
(defn nested->ds [mapvec]
(->> mapvec
(transform [(subselect TOPSORT3 :tempid) (sp/view count)]
#(range (- %) 0))
(select TOPSORT3)
(transform [ALL MAP-VALS (sp/pred :tempid) (collect-one :tempid)]
(fn [v _] v))))
so, I’ve got each of my nodes already pulled out and selected using a recursive cond-path
yea it's close
by the way you can write TOPSORT3 more concisely
this should be equivalent
(declarepath TOPSORT3)
(providepath TOPSORT3
[(cond-path
map? (stay-then-continue MAP-VALS)
vector? ALL)
TOPSORT3])
oh nvm, those are not equivalent
that would stack overflow
when it's on a map, it will first stay
then execute TOPSORT3 again
then stay, then TOPSORT3, etc.
with yours it stays, does whatever is after TOPSORT3, then continues recursing on the map vals
anyway, i gotta run
here's a gist with the solution if you can't figure it out https://gist.github.com/nathanmarz/16481b3b4578ed446453bd828335eedb
that's the same as saying (pred :a)
but that doesn't account for key existing with nil or false value
if you want to truly do it based on key existing, use (selected? (must :a))
or (not-selected? (must :a))
#(:a %)
is equivalent to (pred :a)
@nathanmarz: Made a few updates to https://github.com/codonnell/specter/wiki/List-of-Macros after rereading your post and looking through magic-precompilation
and magic-precompilation*
. I'm just missing examples for path
, and I'm not clear yet on the difference between comp-paths
and path
. Can path
not return a ParamsNeededPath
?
On a side note, there's a typo at https://github.com/nathanmarz/specter/blob/0.11.2/src/clj/com/rpl/specter/impl.cljx#L1150 (s/consructor/constructor)
@codonnell: path
is basically comp-paths
plus inline factoring and caching
You could use path
on its own like this:
(defn user-path [u]
(path ALL (selected? :id #(= u %))))
user-path
will use inline caching + factoring and efficiently return a path to that particular user
select
, transform
, etc. all use path
@codonnell: merged the new wiki page in
fyi, there will be a new core operation in 0.12.0 called traverse
@nathanmarz re: transducers. I agree that the mapcat behavior is the most useful but the name traverse
doesn't seem to imply that behavior.
What's really happening is you are traversing some path on ALL structures coming on some stream (or collection, etc) then getting all those results. Maybe traverse-all
is a more appropriate name?
@aengelberg: yea, that's a better name
or go for the pun and call it transverse
👏 🎉 i'm here all week
@nathanmarz: after creating your user-path
function above, I get an error while trying to use it:
(select (user-path "chris") [{:id "nick"} {:id "foo"} {:id "chris"}])
Failed to cache path: Var user-path must be either a parameterized navigator, a higher order pathed constructor function, or a nav consructor
@codonnell: use compiled-select
alright, that works
yea, I only gave that as an example of using path
on its own
but in general, to make a parameterized navigator it's much better to use comp-paths
and I should use compiled-select
with a path I have already compiled with comp-paths
?
if you know it's already compiled then that will be the most efficient
Also, if I'm understanding path
correctly, it doesn't take late binding parameters because it needs to cache the code which creates the parameters at compile time. Is that right?
select* is like compiled-select except it will compile the input path if it's not already compiled
yea path
is meant for a fully parameterized path
So is there any reason to use select
over select*
?
select* doesn't do inline caching
Right, because it's a function rather than a macro.
99.9% of usage should be through the core select
, transform
, etc. operations
Alright, thanks for answering my questions. :thumbsup:
in my own usage I use select/transform/replace-in over 400 times, comp-paths 15 times, and compiled-select/compiled-transform only twice
@nathanmarz: I added some examples and a longer description for the path
macro, as well as some minor formatting changes and a fix for broken links in the list of navigators. Can you think of a situation where it would be prefer able to use path
over comp-paths
?
Hi everyone. I am stuck with a specter problem i can not get a solution for. I have a simple tree structure as a map and the leaves can be at any level (they are a map with a :template key). I can use a walker to simply find the leaves, but for every leaf, i need the path to that leaf. Is there something like collect-one i can use for this?
(def mytree {"e1" {"e2" {"e1" {:template 1}
"e2" {:template 2}}}})
(select [(walker #(contains? % :template))] mytree)
@thomasdeutsch: what would you like to be returned for that data structure?
i would like to return a vector of maps. The maps have templates + a path to that template. Like [{:template 1 :path ["e1 "e2" "e1]} {:template 2 :path ["e1" "e2" "e2]}]
i think i need a transform with a filterer and a second one that will transform a path into the structure?
I think you want a recursive navigator that collects the keys as you go.
@thomasdeutsch: This is close:
(declarepath KeyAccumWalker [k])
(providepath KeyAccumWalker (if-path must STAY [ALL (collect-one FIRST) LAST (params-reset KeyAccumWalker)]))
(select (KeyAccumWalker :template) mytree)
i have to say, i do not understand if-path must STAY ... but this is a great place to start from....
Thank you
@thomasdeutsch: must
has an implicit late-bound parameter, which is the key :template
@nathanmarz: Do you develop on Linux? (If so, wanted to let you know that we now have an alpha of Planck that runs on Linux. It can successfully load Specter from its JAR and evaluate things. Figure that might help if you ever want to check some behavior related to bootstrap ClojureScript.)
so what's really going on there is more like (if-path (must :template) STAY ...)
aaahhhh, ok! got it.
The problem is that as we recursively call KeyAccumWalker
the collected values are getting nested instead of concatenated.
@codonnell @thomasdeutsch value collection + recursion doesn't work so well in specter at the moment
thanks for the heads up
@mfikes: I develop on OSX, does it work there as well?
Oh… definitely. Planck has worked on OS X all along :) brew install planck
and you are good to go
@nathanmarz: If you ever want to point Planck at your JAR, here’s how:
$ planck -qKc target/specter-0.12.0-SNAPSHOT.jar
cljs.user=> (require-macros '[com.rpl.specter.macros :refer [transform]])
nil
cljs.user=> (require '[com.rpl.specter :refer [ALL]])
nil
cljs.user=> (transform [ALL :a even?] inc [{:a 1} {:a 2} {:a 4} {:a 3}])
[{:a 1} {:a 3} {:a 5} {:a 3}]
That’s all there is to it.
(Note that -K
will cause a .planck_cache
directory to be written for faster subsequent startups. You can also use -k
to write to a specific path, or just leave it off and live with slower require
processing.)ok cool, I'll note this down
Cool. Of course, happy to continue to sort out bootstrap issues if they arise. Just thought you might want to be able to try things yourself if you ever wanted to. 🙂
any leiningen plugin available to be able to do something like "lein planck"?
and get a planck repl with the project code + dependencies available
ah nice, just as good
There’s also fairly decent docs covering this stuff (that’s covered in the Dependencies section). http://planck-repl.org/guide.html