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.
@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]
)))
if you have any structure to your data, I highly recommend making a path tailored as such
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]
@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.
@montanonic yes, those are stand-ins
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]
)))
avoiding parts of data structures that are irrelevant makes a big impact on performance, and also avoids bugs
https://github.com/nathanmarz/specter/wiki/Cheat-Sheet is a good resource for learning what those navigators are
Since I'm not concerned about the values of the keys themselves, would
[(not-selected? (pred= avoid-value)) MAP-VALS p]
also work? sans (keypath avoid-key)
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
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]
Okay, wonderful. Thank you!
I think that makes total sense
with practice this becomes very easy
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!
Is there a better way to collect multiple keys than: [ALL (collect-one :a) (collect-one :b) (collect-one :c)]
, etc..?
@montanonic (specter/collect (specter/multi-path :a :b :c))
Great! thanks 🙂
(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}]]
thanks for asking, I'd never actually tried that before
@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]
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
.