This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-10-17
Channels
- # beginners (45)
- # boot (33)
- # chestnut (9)
- # cider (2)
- # cljs-dev (24)
- # cljsrn (1)
- # clojure (114)
- # clojure-conj (3)
- # clojure-dev (3)
- # clojure-dusseldorf (3)
- # clojure-greece (5)
- # clojure-italy (22)
- # clojure-russia (10)
- # clojure-spec (12)
- # clojure-uk (19)
- # clojurescript (117)
- # core-async (16)
- # cursive (3)
- # data-science (1)
- # datomic (5)
- # docs (13)
- # emacs (1)
- # fulcro (13)
- # graphql (1)
- # hoplon (20)
- # immutant (3)
- # jobs (1)
- # juxt (12)
- # lein-figwheel (1)
- # luminus (4)
- # off-topic (12)
- # onyx (61)
- # portkey (1)
- # re-frame (21)
- # reagent (26)
- # ring-swagger (38)
- # rum (1)
- # shadow-cljs (222)
- # slack-help (4)
- # spacemacs (11)
- # specter (67)
- # uncomplicate (236)
This code does what I want:
(def data (let [inner-data [{:bar #{:a}}
{:bar #{:a :b}}
{:bar #{:c}}]]
{:a inner-data
:b inner-data}))
(setval
[(multi-path :a :b)
ALL
(multi-path
[:bar (selected? ALL
(comp not #{:a :b}))]
empty?)]
NONE
data)
but not in the case of nil
or {}
, then it adds keys with nil@borkdude nil or {} for what?
the inner maps?
not following
(setval
[(multi-path :a :b)
ALL
(multi-path
[:bar (selected? ALL
(comp not #{:a :b}))]
empty?)]
NONE
nil)
you can use (multi-path (must :a) (must :b))
i've found the need to definitely navigate to a key is far more common
and it's faster
also (update {} :a identity) ;; => {:a nil}
Specter 1.0.4 released
@nathanmarz Do you recommend using specter in lieu of clojure.core fns always? Or perhaps only when either 1) it's known to be faster; 2) it provides a much more robust solution;
Based on the benchmarks, it looks like specter isn't always faster. On a team, this makes it harder to bring in, since one can't just say "We're using specter for data access now; it's faster and more robust." Unfortunately, the ruleset of when to use it (i.e. get-in
seems slow, transform
is very fast, unless you're doing a mapv
) is complex, then makes it much more expensive to adopt and learn.
@jeaye I use it always when it's more elegant than trying to do it in vanilla Clojure
the overhead is generally quite small even for cases where the vanilla Clojure is just as elegant
the notable exception is (select-any [:a :b :c] data)
vs. (-> data :a :b :c)
I do believe it's possible to eliminate most of the overhead for that with additional work on Specter
though for a case like that I would do the latter expression in my own code since it's elegant
is there a way to use specter to select only distinct values (besides the obvious of (distinct (specter/select apath structure))
which requires looping through the final result)
(not that this is really a huge performance hit, but just curious)
@tanzoniteblack you can do (into #{} (traverse apath structure))
that's the fastest
not the same though, since set's don't retain the order (which is important in this case)
(which I didn't specify in my question 🙂 , but distinct
does retain the order, which is a useful property in my case)
distinct
is the easiest, though traverse
just returns a reducible so you can do it faster if needed
reduce
over the traverse
return to add into a vector while keeping a mutable set in scope in order to not append duplicates
thanks
actually (into [] (distinct) (traverse apath structure))
might be equivalent to that
yea it is:
user=> (into [] (distinct) (traverse ALL [1 2 3 4 5 2 1 7]))
[1 2 3 4 5 7]
nice to know I can use traverse
with transducers
you can also make a specter-based transducer with traverse-all
@nathanmarz When you do a multi-path over an ALL
, will it iterate multiple times over the sequence? My code now looks like this:
(setval
[(multi-path (must :report) (must :highlights))
ALL
(multi-path
[:pico
#(not (some (set picos) %))]
;; if key is removed, remove the map entirely
(comp not :pico))]
NONE
results)
that does not iterate over any sequence multiple times
I think this is more readable though:
(transform
[(multi-path (must :report) (must :highlights))]
#(remove
(fn [m]
(not (some (set picos) (:pico m)))) %)
results)
@borkdude the specter version can be done much simpler:
(setval
[(multi-path (must :report) (must :highlights))
ALL
(selected? :pico ALL (pred (set picos)))]
NONE
results)
The problem is that I need something slightly different:
(def picos [:a :b])
(def results {:highlights [{:pico [:a]} ;; keep
{:pico [:a :b]} ;; keep
{:pico [:c]} ;; drop
{:pico [:c :d]} ;; drop
{:pico [:c :a]}]}) ;;keep
(transform
[(multi-path (must :report) (must :highlights))]
#(remove
(fn [m]
(not (some (set picos) (:pico m)))) %)
results) ;;=> {:highlights ({:pico [:a]} {:pico [:a :b]} {:pico [:c :a]})}
ah you just need (not-selected? ALL (set picos))
btw, the pred
is only there for performance, and is only needed if picos
is a local variable
the specter version will also preserve the sequence type
also will only convert picos
to a set once, instead of for every element
What is the explanation of this result? I don’t understand selected?
and not-selected?
well enough.
(select [(selected? ALL odd?)] [1 2 3]) ;;=> [[1 2 3]]
selected?
stays navigated if the path selects at least one value
in this case it's run on the root, and it is true
so it selects the root [1 2 3]
so with a local I need (pred (set picos))
and then it will substitute the value of the expression?
the pred
in that case avoids a runtime conversion from an implicit navigator (a set) to a navigator
sets are implicitly wrapped in pred
, but if the value at a spot is dynamic, then specter cannot know that until runtime
it's not a huge deal, but it is a protocol invocation