Filter a Map in Clojure
Have some keys dragging you down? Let's filter those bad guys out. This is Clojure. Ready your parens.
The Map
(def articles
{:a {:draft "false"}
:b {:draft "true"}
:c {:draft "true"}
:d {:draft "false"}})
Looping Through the Map
To do this, use a for
expression. See the Looping Through a Map in Clojure article for more explanation. The result will look like this:
(for [[k v] articles] v)
; => ({:draft "false"} {:draft "true"} {:draft "true"} {:draft "false"})
The Filter
But I don't just want to loop, I want to filter. So let's first define a filter that's useful for this map data:
(defn is-published? [x]
(not= "true" (:draft x)))
Filter the Map
We'll apply the filter with a :when
modifier:
(for [[k v] articles :when (is-published? v)] v)
; => ({:draft "false"} {:draft "false"})
Filter and Keep Keys and Values
But when we "filter a map", we want a map to come out the other side, not a sequence of the values in the original map.
To do this, we need to filter to keys instead of values, as we've been doing. And then we pass that sequence of keys to another api, select-keys.
Its function signature:
(select-keys map keyseq)
We'll pass the original map and then the filtered keys expression from the for
loop:
(select-keys articles (for [[k v] articles :when (is-published? v)] k))
; => {:a {:draft "false"}, :d {:draft "false"}}
(Note that important change to return k
from the for
expression.)
Yay!, we filtered the map.
There's gotta be 799 different ways to do that. Is there a more readable or more elegant way that you prefer?