specter

Latest version: 1.1.3
montanonic 2018-05-16T08:09:57.000084Z

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.

nathanmarz 2018-05-16T13:55:24.000721Z

@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]
               )))

nathanmarz 2018-05-16T13:56:18.000599Z

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

montanonic 2018-05-16T19:06:13.000696Z

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]

montanonic 2018-05-16T19:18:40.000618Z

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

nathanmarz 2018-05-16T19:21:42.000424Z

@montanonic yes, those are stand-ins

nathanmarz 2018-05-16T19:21:51.000127Z

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

nathanmarz 2018-05-16T19:22:30.000549Z

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

nathanmarz 2018-05-16T19:22:54.000727Z

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

montanonic 2018-05-16T19:23:01.000640Z

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

montanonic 2018-05-16T19:23:08.000686Z

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

montanonic 2018-05-16T19:23:14.000731Z

also work? sans (keypath avoid-key)

montanonic 2018-05-16T19:23:47.000459Z

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

nathanmarz 2018-05-16T19:24:09.000583Z

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]

montanonic 2018-05-16T19:24:51.000188Z

Okay, wonderful. Thank you!

montanonic 2018-05-16T19:25:07.000275Z

I think that makes total sense

nathanmarz 2018-05-16T19:25:34.000678Z

with practice this becomes very easy

1
montanonic 2018-05-16T19:27:11.000402Z

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!

montanonic 2018-05-16T21:23:35.000061Z

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

tanzoniteblack 2018-05-16T21:26:34.000478Z

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

montanonic 2018-05-16T21:26:48.000351Z

Great! thanks 🙂

tanzoniteblack 2018-05-16T21:27:08.000220Z

(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}]]

tanzoniteblack 2018-05-16T21:27:24.000245Z

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

🙂 1
nathanmarz 2018-05-16T21:45:57.000389Z

@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]

1
montanonic 2018-05-16T23:41:43.000152Z

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.