Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you combined sort-by and filter in Clojure?

Tags:

clojure

Let's say I have a collection of strings, and I want to return all strings over 4 characters long, sorted by shortest string first.

You could solve that with something along the lines of:

(def strings ["this" "is" "super" "cool" "everybody" "isn't" "clojure" "great!?!?"])
(sort-by count < (filter #(> (count %) 4) strings))
;; > ("super" "isn't" "clojure" "everybody" "great!?!?")

Notice we're using count twice. This is probably fine here, but what if count wasn't count? What if instead of count we called super-expensive-function that we'd really rather not run more than absolutely necessary?

So:

  • We have a collection of things
  • We want to return an ordered collection of things
  • Filtered and sorted using the result of a computationally expensive function which should only be called once per thing

Is there an existing function that does this, or do I need to build my own?

like image 478
SCdF Avatar asked Jan 12 '23 16:01

SCdF


1 Answers

The simplest solution would be to pair up each item together with its expensive-to-compute property, then filter and sort, then discard the parameter:

(->> strings
     (map (juxt identity count))
     (filter (fn [[_ c]] (> c 4)))
     (sort-by peek)
     (map first))

If computing the property in question is really all that expensive, the overhead of allocating the vectors should pretty much vanish.

like image 78
Michał Marczyk Avatar answered Jan 23 '23 15:01

Michał Marczyk