specter

Latest version: 1.1.3
2018-01-06T11:33:46.000038Z

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.

2018-01-06T11:35:20.000079Z

I want to reach for if-path but don't think I can use setval with that to conditionally set :a or NONE

2018-01-06T11:38:09.000020Z

Or should I just stick to using maps?

2018-01-06T11:44:37.000054Z

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).

2018-01-06T12:09:09.000107Z

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.

schmee 2018-01-06T12:23:22.000006Z

@chromalchemy I think you can use https://github.com/nathanmarz/specter/wiki/List-of-Macros#multi-transform for this

2018-01-06T13:28:07.000016Z

@nathanmarz Thank. Can I use multi-transform with if-path?

2018-01-06T13:28:26.000031Z

instead of multi-path in the examples...

2018-01-06T13:28:48.000045Z

right now I'm hung up on:

(select
    (if-path (= 1 2) ; false condition
      [ALL (pred= :a)]
      [ALL (pred= :c)])
    [:a :b :c])
=> [:a]

2018-01-06T13:30:22.000033Z

Doesn't seem to want to return the second path, based on the negative condition

2018-01-06T13:30:59.000040Z

Oh I think I see. It must be a conditional-path, not a generic condition...

2018-01-06T14:11:54.000030Z

This is working!

(multi-transform
  (if-path
    [ ALL (pred= :a)]
    [ ALL (pred= :a) (terminal-val NONE)]
    [ BEFORE-ELEM (terminal-val :a)])
  [:a :b :c])

2018-01-06T14:14:45.000052Z

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.

nathanmarz 2018-01-06T14:16:15.000051Z

@chromalchemy if ordering isn't important, then using a set or multi-set would be much more natural

schmee 2018-01-06T21:34:08.000057Z

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)

nathanmarz 2018-01-06T22:38:38.000004Z

traverse-all runs on each item of sequential input

nathanmarz 2018-01-06T22:38:57.000064Z

@schmee which is why is has the -all suffix

nathanmarz 2018-01-06T22:41:11.000032Z

that's generally what transducers do

schmee 2018-01-06T22:43:52.000029Z

ok, I think I see the difference now

schmee 2018-01-06T22:46:54.000080Z

so traverse-all can be transduced but traverse is reducible but not transducible?

schmee 2018-01-06T22:47:21.000033Z

if that makes any sense

nathanmarz 2018-01-06T22:50:23.000120Z

traverse-all creates a transducer

nathanmarz 2018-01-06T22:50:30.000064Z

traverse returns a reducible object

nathanmarz 2018-01-06T22:51:04.000017Z

traverse-all exists to integrate specter with transducers

schmee 2018-01-06T23:47:20.000010Z

gotcha, thanks for clarifying 👍