Fork me on GitHub
#specter
<
2018-05-16
>
montanonic08:05:57

I'm new to Specter and trying to figure out how to express finding all of the numbers in deeply nested data, except for those that are nested within maps (at any depth) containing a particular value. Finding all the numbers is totally easy, `(traverse (walker number?) data)`, it's the filtering out part that I'm struggling with.

nathanmarz13:05:24

@montanonic if you truly want to do a blind walk (including into map keys, key/value pairs) then you can do it like this:

``````(def my-walker
(recursive-path [afn] p
(cond-path (pred afn) STAY
map? [(not-selected? :key (pred= :val)) ALL p]
coll? [ALL p]
)))
``````

nathanmarz13:05:18

if you have any structure to your data, I highly recommend making a path tailored as such

montanonic19:05:13

The problem I'm solving is just a for-fun code challenge with a pathologically nested data set; I'm not sure how "structured" it is. Thanks for your help! I'm going to need to spend a bit of time to understand what's going on there, particularly `map? [(not-selected? :key (pred= :val)) ALL p]`

montanonic19:05:40

@nathanmarz Can you elaborate more on `:key` and `:val` in that code? Did you intend them to just be stand-ins? I also don't, in this case, need to consider map keys, only values.

nathanmarz19:05:42

@montanonic yes, those are stand-ins

nathanmarz19:05:51

this is better:

``````(def my-walker
(recursive-path [afn avoid-key avoid-value] p
(cond-path (pred afn) STAY
map? [(not-selected? (keypath avoid-key) (pred= avoid-value)) MAP-VALS p]
coll? [ALL p]
)))
``````

nathanmarz19:05:30

avoiding parts of data structures that are irrelevant makes a big impact on performance, and also avoids bugs

nathanmarz19:05:54

https://github.com/nathanmarz/specter/wiki/Cheat-Sheet is a good resource for learning what those navigators are

montanonic19:05:01

Since I'm not concerned about the values of the keys themselves, would

montanonic19:05:08

`[(not-selected? (pred= avoid-value)) MAP-VALS p]`

montanonic19:05:14

also work? sans ` (keypath avoid-key)`

montanonic19:05:47

Thanks for that resource! I've been reading all the wiki pages repeatedly; they are very good; I just found my use-case to be one of the ones that was harder to figure out from them

nathanmarz19:05:09

if you're avoiding maps which contain a specific value for any key, then do `[(not-selected? MAP-VALS (pred= avoid-value)) MAP-VALS p]`

montanonic19:05:51

Okay, wonderful. Thank you!

montanonic19:05:07

I think that makes total sense

nathanmarz19:05:34

with practice this becomes very easy

4
montanonic19:05:11

Wow, totally works. That's super great. I was struggling to figure out how to filter values, but I can see how `selected` and `not-selected?` help with that. Super!

montanonic21:05:35

Is there a better way to collect multiple keys than: `[ALL (collect-one :a) (collect-one :b) (collect-one :c)]`, etc..?

tanzoniteblack21:05:34

@montanonic `(specter/collect (specter/multi-path :a :b :c))`

montanonic21:05:48

Great! thanks 🙂

tanzoniteblack21:05:08

``````(specter/select [specter/ALL (specter/collect (specter/multi-path :a :b :c))]
[{:a "cat" :b "dog" :c "c"}
{:a 1 :b 2 :c 3}])``````
==>
``````[[["cat" "dog" "c"] {:a "cat", :b "dog", :c "c"}]
[[1 2 3] {:a 1, :b 2, :c 3}]]``````

tanzoniteblack21:05:24

thanks for asking, I'd never actually tried that before

🙂 4
nathanmarz21:05:57

@montanonic @tanzoniteblack can also use `eachnav`:

``````(select-any [((eachnav collect-one) :a :b :c) :d] {:a 1 :b 2 :c 3 :d 4})
;; => [1 2 3 4]
``````

4
montanonic23:05:43

Is there a more efficient or idiomatic way to do this with specter? `(every? #(= 2 %) (select [MAP-VALS] coll))` That is: tell me if all of the values in a `coll` that is a map are equal to some value, in this case `2`.