This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-07-26
Channels
- # bangalore-clj (1)
- # beginners (12)
- # boot (48)
- # cider (56)
- # clara (1)
- # cljs-dev (15)
- # clojure (455)
- # clojure-austin (2)
- # clojure-dev (33)
- # clojure-italy (26)
- # clojure-nl (6)
- # clojure-poland (10)
- # clojure-russia (23)
- # clojure-spec (33)
- # clojure-uk (62)
- # clojurescript (37)
- # code-art (2)
- # cursive (12)
- # datomic (48)
- # funcool (1)
- # juxt (16)
- # leiningen (13)
- # off-topic (12)
- # om (23)
- # onyx (16)
- # other-lisps (5)
- # parinfer (2)
- # pedestal (28)
- # re-frame (60)
- # reagent (8)
- # ring (1)
- # ring-swagger (15)
- # spacemacs (5)
- # specter (53)
- # test-check (2)
- # unrepl (8)
- # vim (14)
hi guys
I'm trying to implement something like this
(get-capitals {"A" ["hello"]
"b" ["other"]})
;; should return {"A" ["hello"]}
with Specter, which in theory it's just a select right?
(defn get-capitals
[probs]
(specter/select
[specter/MAP-KEYS #(Character/isUpperCase (first %))]
probs))
this loses the original structure though
select
always return a sequence, so if you want to maintain the structure you have to use something else
ah yes ok i can try with transofrm
but the function itself should be identity then?
this should do it:
(defn get-capitals
[probs]
(specter/setval
[specter/MAP-KEYS #(Character/isLowerCase (first %))]
specter/NONE
probs)
Ah this also works actualy
(defn get-capitals
[probs]
(specter/transform
[specter/MAP-KEYS #(Character/isUpperCase (first %))]
identity
probs))
I actually need both submaps really
or maybe I can just use select to extract the two set of keys and reconstruct the maps later
I have a straight-forward selector that, does not function as I expect it to when the last element is index reference to a collection. Here is the select
without the index in the keypath
followed by the select
with the index (0) in the keypath
:
(sp/select [(apply sp/keypath [:db :right :text-entry 0 :offset])])
[({:text "180 West", :label ""})]
(sp/select [(apply sp/keypath [:db :right :text-entry 0 :offset 0])])
[nil]
The index navigator after :text-entry
always works. It seems like it is just when the index is at the last position.@frankmoyer what's the input?
@nathanmarz I just figured out what was going on while I was compiling the input for you. A prior transform
altered the portion of the structure that was being modified to be a List
. When I convert it to a vec
in the form, subsequent transform
operations are able to navigate to the index. Thank you!
is there a way to make walker search only maps (not e.g. Datomic entitymaps?)
(def link-walker [(sp/walker ::link)])
@frankmoyer cool, figured it would be something like that
I’m confused why these give different results:
dev.user=> (sp/transform [(sp/walker ::link)] identity {:a 1 :b (d/entity (db/db) 17592186045447) ::link [:a 1]})
{:a 1, :b #:db{:id 17592186045447}, :dev.user/link [:a 1]}
dev.user=> (sp/transform [(sp/walker ::link)] identity {:a 1 :b (d/entity (db/db) 17592186045447)})
AbstractMethodError clojure.lang.RT.conj (RT.java:667)
oh nevermind, I think I know why-- depth first search descends in the second case but cuts off in the first case
@wei walker will blindly walk into any collection (functions like clojure.walk)
thanks @nathanmarz. looking for a good alternative to walker
then
https://github.com/nathanmarz/specter/blob/master/src/clj/com/rpl/specter.cljc#L1352
(walker ::link)
doesn't make much sense
look at the docstring
it's generally better to make your own recursive path tailored to the data structure / problem at hand
and it's really easy to do
I’m trying to find maps with the key ::link
in them, so it seems like a legit afn
to me? could you explain what I’m missing?
(in my example above, the map passed in was contrived and I substituted identity
with a function that actually does something useful)
it's going to call ::link
on all values / data structures it sees
e.g. (::link 1)
I wouldn't rely on that behavior
#(and (map? %) (contains? % ::link))
is better i think
ah that makes more sense. unfortunately it’s still descending into the datomic entity I think. is there a way to make walker
short-circuit, or is there a better alternative?
dev.user=> (sp/transform [(sp/walker #(and (map? %) (contains? % ::link)))] identity {:a 1 :b (d/entity (db/db) 17592186045447)})
AbstractMethodError clojure.lang.RT.conj (RT.java:667)
you can't avoid that with walker
an easy variant you could use is:
(def walk-skipper
(recursive-path [afn skip-fn] p
(cond-path (pred skip-fn) STOP
(pred afn) STAY
coll? [ALL p]
)))
set the skip-fn
to return true on datomic entities
or anything else you don't want to walk
pred
is a navigator
that's what's implicitly used when you put a function into a path
it's used in this case to avoid runtime conversion of function -> navigator
(for performance)
great, seems to be working as expected! i substituted com.rpl.specter/pred
so I could define walk-skipper
in my own ns. thanks for your help.
sorry to bug you again @nathanmarz but the walk-skipper
doesn’t seem to be working for me:
(def walk-skipper
(sp/recursive-path [afn skip-fn] p
(sp/cond-path (sp/pred skip-fn) sp/STOP
(sp/pred afn) sp/STAY
coll? [sp/ALL p])))
dev.user=> (sp/select [(sp/walker #(and (map? %) (contains? % :d)) )] {:a 1 :b 2 :c {:d 1}})
[{:d 1}]
dev.user=> (sp/select [(walk-skipper #(and (map? %) (contains? % :d)) (complement map?))] {:a 1 :b 2 :c {:d 1}})
[]
@wei the definition of walker
/ walk-skipper
traverses all collections via ALL
to replicate clojure.walk semantics
so that means it traverses each key/value pair as a vector, then traverses those to each key/value
since you stop navigation if encountering a non-map, it stops traversing once it reaches key/value pairs
so never reaches the inner map
like I said, it's better to make your own recursive path tailored to the data structure / problem at hand
in this case you probably want to descend using MAP-VALS
rather than ALL