I love juxt
1. But I was reminded yesterday that not everyone
has made friends with it. So, to give it a little of the limelight it
deserves, I thought I'd write down My Favourite Juxt Trick.
First, let's recap what juxt
does: it lets you pass the same
arguments to several functions in a row. So if you want to find out a
person's name & height, you can do this:
(def name-and-height
(juxt :name :height))
name-and-height
is now a single function that returns both results
as in a vector:
(def person {:name "Kris"
:height 172
:devastating-handsomeness-factor 2})
(println
(name-and-height person))
;=> [Kris 172]
Nothing special there. It's just calling each function, :name
and
:height
, with the same arguments, and gathering up the results.
Now let's do something cool with it. I've got a list of people working in different departments:
(def people [{:name "Brad" :salary 27000 :department "hats"}
{:name "Janet" :salary 54000 :department "capes"}
{:name "Eddie" :salary 19500 :department "hats"}
{:name "Frank" :salary 98000 :department "capes"}
{:name "Rocky" :salary 18000 :department "shoes"}])
I'd like to roll those up by department and largesse. First let's define what counts as highly-paid, using a number that's entirely fictional in London:
(defn highly-paid? [x]
(< 30000 (:salary x)))
Now we have two functions, and we can group by both of them:
(pprint
(group-by (juxt :department highly-paid?)
people))
{["hats" false]
[{:name "Brad", :salary 27000, :department "hats"}
{:name "Eddie", :salary 19500, :department "hats"}],
["capes" true]
[{:name "Janet", :salary 54000, :department "capes"}
{:name "Frank", :salary 98000, :department "capes"}],
["shoes" false] [{:name "Rocky", :salary 18000, :department "shoes"}]}
There - we've just built an on-the-fly, composite-key index of our data, for free.
Any time you need to filter some data by multiple criteria, juxt
comes in mighty handy.