What's a good way to conditionally add/remove a value based on whether it's already in a collection? Basically like assoc
or dissoc
for vectors, based on value instead of index. Example: If :a
is not it in [:b :c]
, add it, else remove it.
I want to reach for if-path
but don't think I can use setval
with that to conditionally set :a
or NONE
Or should I just stick to using maps?
I'm trying to filter some items on some keywords, and I wanted to avoid having {:a true :b false}
or {:a nil :b :nil}
cluttering my data up. I feel like map values are redundant, because I can get boolean truthiness by testing for the presence of keywords (in maps at least).
Probably I should use a set instead of a vector. But don't think that deserializes from Firebase properly, which is what I'm using for persistence.
@chromalchemy I think you can use https://github.com/nathanmarz/specter/wiki/List-of-Macros#multi-transform for this
@nathanmarz Thank. Can I use multi-transform
with if-path
?
instead of multi-path
in the examples...
right now I'm hung up on:
(select
(if-path (= 1 2) ; false condition
[ALL (pred= :a)]
[ALL (pred= :c)])
[:a :b :c])
=> [:a]
Doesn't seem to want to return the second path, based on the negative condition
Oh I think I see. It must be a conditional-path, not a generic condition...
This is working!
(multi-transform
(if-path
[ ALL (pred= :a)]
[ ALL (pred= :a) (terminal-val NONE)]
[ BEFORE-ELEM (terminal-val :a)])
[:a :b :c])
Do you think I am barking up the wrong tree performance-wise to use keywords in vectors this way? The set of keywords will be pretty small, so I hope sequential lookup with (pred=)
is not too big a drag.
@chromalchemy if ordering isn't important, then using a set or multi-set would be much more natural
hmm… am I doing something wrong here or is this supposed to work?
user=> (def data {:a [:x] :b [:x :x] :c [:x :x :x]})
#'user/data
user=> (select [MAP-VALS (view count)] data)
[1 2 3]
user=> (reduce + (traverse [MAP-VALS (view count)] data))
6
user=> (transduce (traverse-all [MAP-VALS (view count)]) + 0 data)
ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry clojure.lang.APersistentMap$ValSeq.first (APersistentMap.java:234)
traverse-all
runs on each item of sequential input
@schmee which is why is has the -all
suffix
that's generally what transducers do
ok, I think I see the difference now
so traverse-all
can be transduced but traverse
is reducible but not transducible?
if that makes any sense
traverse-all
creates a transducer
traverse
returns a reducible object
traverse-all
exists to integrate specter with transducers
gotcha, thanks for clarifying 👍