hey folks, given a vector of vectors of integers, and a separate vector of integers to remove, how can I remove all integers from the sub-vectors that are in the separate vector?
(def to-remove [ 4 5 6 ])
(def coll [ [1 2 4] [3 5 8 ] [6] [] ])
(specter/transform [(specter/filterer not-empty) specter/ALL ..?.. ] specter/NONE coll)
;; => [ [1 2] [3 8] [] [] ]
Seems like this works, is there a more efficient way to write it?
(specter/setval [specter/ALL specter/ALL (fn [val] (some #(= val %) to-remove))] specter/NONE coll)
Might be nicer if you made to-remove
a set, then it is just (to-remove val)
or replace fn
form with #(to-remove %)
Hi, sorry if this is a FAQ, but how can I navigate to a map, and then filter the entries in the map, based on certain nested values in the values?
Here’s what I’m doing in “plain old Clojure” (plus Medley) that I’m curious how to translate to Specter:
(->> (db/read "db")
(:technologies)
(medley/filter-vals
(fn [tech] (some (fn [rec] (rec "ratified"))
(get tech "recommendations")))))
Thanks!
not sure it's the most straightforward approach, but you could probably use:
[:technology MAP-VALS]
for the path. in your transform function:
• return the original value to keep it
• return spec/NONE to remove a valueThanks! But I’m not looking to transform, just to query.
oh
Yeah, I left if off but when I ran the above code, the last form in the thread-last form was keys
[:technology MAP-VALS (fn [tech] (some (fn [rec] (rec "ratified"))
(get tech "recommendations")))]
interesting, I thought I tried that… I’ll try again, brb!
i'm not specter expert either ¯\(ツ)/¯
Wait… that’ll navigate to the results of the predicate, right? But I want the map under :technologies
, just filtered
I’m very new to Specter but I’d think it’d have something roughly equivalent to Medley’s filter-vals
(which filters a map by applying a predicate to the vals in the map, and returns a map containing the matching entries)
(Or something equivalent to filter
that I could use to the same effect)
https://github.com/redplanetlabs/specter/wiki/List-of-Navigators#filterer
Right! I tried that but had trouble with it. Thought maybe I just didn’t understand it. I’ll try again… brb
imo, specter is much more useful if you're transforming or setting data in a deeply nested data structure. if you're just drilling into a nested data structure, I usually just use the normal clojure functions
that makes sense.
But I’d like to build up my own intuition as to when to use Specter, by seeing what it’s like to use it in cases like this
:thumbsup:
Also, I had to walk a Clojure newbie through the above code and it was non-trivial to explain. I think the path concept could maybe be much more straightforward for folks new to Clojure and Lisps.
i'm only a specter novice, so there's probably a very straightforward way to do it that I don't know about
I hear ya, I have the same suspicion
Trying this:
(select [:technologies
(filterer MAP-VALS "recommendations" ALL "ratified")
MAP-KEYS]
db)
getting: java.lang.ClassCastException: "class java.lang.String cannot be cast to class java.util.Map$Entry…
specter doesn't recognize strings as map keys like it does for keywords
really?
I think "recommendations" needs to be (keypath "recommendations")
I thought i saw that work, one sec…
hmmm, maybe it does
Yeah, this works: (select [:technologies MAP-VALS "recommendations" ALL] db)
Is there away to setval on a path but only set the first matching value?
Using my example from before, if coll was (def coll [ [1 2 4] [3 5 8 4] [6] [] ])
I'd like the result to be ;; => [ [1 2] [3 8 4] [] [] ]
(that is, the 4 is only removed once)
If I add a FIRST
after the (fn ...)
in the path, I get a Error: 4 is not ISeqable
filterer is complaining because it's turning the map into a sequence
maybe something like:
(spec/select [:technologies
(spec/filterer (spec/nthpath 1) "recommendations" spec/ALL "ratified")
ALL
(spec/nthpath 0)]
db)
huh, that’s surprising. I wonder why it’s doing that. I’d think that’d be antithetical to Specter’s general behavior of leaving types as-is
also: thank you!
interesting… this “works”, but the result is incorrect:
(select [:technologies
(filterer (nthpath 1) "recommendations" ALL "ratified")
MAP-KEYS]
db)
shoot
> with the path yields anything other than an empty sequence.
the path is probably yielding false
the path in filterer
?
right
I wouldn’t think so…?
why would it yield false?
I thought any path to something that doesn’t exist yields nil?
or whatever is in the ratified key
does the ratified key exist even for unratified recommendations?
right. but most of the maps in the sequences that correspond to the "recommendations" keys do not have the key "ratified"; only a few do. I’m trying to find those technologies that have recommendations that have been ratified
this seems to work:
(def data {:technologies
{0 {"recommendations" [{"ratified" true}]}
1 {"recommendations" [{"ratified" false}]}
2 {"recommendations" []}}})
(spec/select [:technologies
(spec/filterer (spec/nthpath 1) "recommendations" spec/ALL "ratified" identity)
spec/MAP-KEYS
;; ALL
;;(spec/nthpath 0)
]
data)
the above will return [0]
it does!
I don’t get it though 🙃
why is the identity
needed? what is it doing?
so without identity and removing filterer:
(def data {:technologies
{0 {"recommendations" [{"ratified" true}]}
1 {"recommendations" [{"ratified" false}]}
2 {"recommendations" []}}})
(spec/select [:technologies
ALL
(spec/nthpath 1) "recommendations" spec/ALL "ratified"
;; spec/MAP-KEYS
;; ALL
;;(spec/nthpath 0)
]
data)
you get [true false]
That’s not what I’m getting
basically, it can navigate to the "ratified" key
oh wait…
I also removed the filterer
ah interesting
and the path exists if it can navigate there
I see, you did that as a way to debug the filterer path?
so filterer will keep paths that it can navigate to, even if the value it navigates to is falsey
cool, that’s clever, makes sense!
that’s… surprising
but, so be it
so identity is the simplest function that I can think of to filter based on truthiness
I think filterer has the right design, but it is a little surprising at first
some?
?
(some? false)
=> true
actually, boolean
is probably clearer
😵
(def data {:technologies
{0 {"recommendations" [{"ratified" true}]}
1 {"recommendations" [{"ratified" false}]}
2 {"recommendations" []}}})
(spec/select [:technologies
(spec/filterer (spec/nthpath 1) "recommendations" spec/ALL "ratified" boolean)
spec/MAP-KEYS
]
data)
some?
's doc string is correct:
clojure.core/some?
[x]
Added in 1.6
Returns true if x is not nil, false otherwise.
So some?
is equivalent to (complement nil?)
…. ?
Anyway, I still don’t quite get filterer
— but a prerequisite to getting it is seeing what works. Which I’ve got now. Thank you so much!