I've been using this pattern quite a lot in my code, but it looks really ugly to me:
(some #(when (= (:id %) foo-id) %) foos)
The JavaScript equivalent feels a lot less messy:
foos.find(f => f.id == foo-id)
Is there a more idiomatic way to do this? I could always write my own find, but there's no way that's the idiomatic way to do it.
filter + first would be an alternative, but that too seems too verbose.depends on what you are doing, but I would start by asking if you actually need more than one call to some
e.g. should it be something like (->> ... (map ...) (filter ...) (keep ...) (mapcat ..) (some ...))
I am simply getting the foo from my vector of foos in various places in my app. The reason foos is a vector and not a map is because order matters. I could denormalize the state and have both but that's way overkill I feel.
🤷 if you have a pattern you wish was more concise write a function
you can also write your own find . i also get annoyed at the some pred return object paradigm. but trivial to make your own
I just want to emulate what the Clojure elite does rather than make my own home-grown solutions at this point. Surely doing O(n) searches in vectors isn't that uncommon in Clojure even if we all love maps here?
the clojure elite make home grown solutions
but seriously. the find function in js there is a fine one. It doesn’t exactly mash well with some of the sequence operations which might be why it’s not in the core namespace
medley has find-first: https://github.com/weavejester/medley/blob/822981871facb27630dcba03cce2924a34989963/src/medley/core.cljc#L7
most of the time, (first (filter ...)) is good enough for me
if i'm in a performance sensitive spot, i'll use (reduce (fn [_ cur] (when (pred cur) (reduced cur))) nil coll) (which is how find-first is implemented)
How come that's more performant? Does first not leverage the laziness benefits of filter?
reduce has fast path impls for some things
This is one of those sticklers that the Clojure team has where they just refuse to put an O(n) find function in core. There's been many who asked or debated, and so on. The grown up approach is to write your own. I would say, if your list is sorted though, and it's a vector, you can implement a binary search find and get O(logN)
lazy seqs allocate and require additional computation to traverse, while reduce+reduced doesn't, reduce has fast impls for certain sequence types, clojure's laziness can do chunking which will compute additional elements in the seq before selecting the 'first'
It's hard to explain, but the simplest variant is that lazy seqs will wrap the elements in batches of 32. That wrapping/unwrapping adds a little bit of overhead. Reduce iterates the list without this wrapping.
I see, thanks. I suspect I will decide on going the "overkill" route (which I mistook for denormalization) and manage a map for the lookup and a separate id vector for the ordering. Maybe that's a good pattern to follow in general even for tiny vectors due to separation of concerns.
Since nobody has mentioned it so far, maybe I'm the weird one? I use filter+first but for the pred I like comp + set literal:
(first (filter (comp #{foo-id} :id) foos))reduce is technically more general than seq traversal too
I use this oneliner in lots of places, easy to drop into a util lib or letfn
(defn key= [k v] #(when (= (k %) v) %))
Which lets you write eg.
(some (key= :id "foo-id") foos)
which I find more readable than a lot of the alternatives - and also composes nicely with keep , filter , some-fn etc.