Is there a cleaner way to filter a map?

4,243 views
Skip to first unread message

Conrad

unread,
Sep 1, 2009, 7:35:44 PM9/1/09
to Clojure
Hi everyone! I was wondering if there was a better idiom in Clojure
for filtering items from a map... Suppose we want to remove all items
from a map that have an odd number as a value. Here's how I'd write
it:

=> (apply hash-map
(apply concat
(filter (fn [[key val]]
(even? val))
{:dog 5 :cat 4 :mouse 7 :cow 6})))

{:cow 6, :cat 4}

This is ugly. Is there a more elegant way to write this kind of code?
Please share!

Thanks!

Richard Newman

unread,
Sep 1, 2009, 7:44:10 PM9/1/09
to clo...@googlegroups.com
Conrad,

(into {}


(filter (fn [[key val]]
(even? val))
{:dog 5 :cat 4 :mouse 7 :cow 6}))

=>
{:cat 4, :cow 6}

There's probably also a higher-order function that does what you want
in contrib, something like

(defn filter-map-v [f m]
(into {}
(filter (fn [[k v]] (f v)) m)))

so that you can write

(filter-map-v even?


{:dog 5 :cat 4 :mouse 7 :cow 6})

I haven't looked for it, though.

-R

Sean Devlin

unread,
Sep 1, 2009, 8:04:02 PM9/1/09
to Clojure
It's currently being discussed in contrib, but noting is final yet.

Conrad

unread,
Sep 1, 2009, 8:09:02 PM9/1/09
to Clojure
Thanks for the info- Using "into" definitely cuts down a lot on the
ugliness.

Timothy Pratley

unread,
Sep 1, 2009, 8:17:13 PM9/1/09
to Clojure
Hi Conrad,

I find this an interesting question because there is already a select
function in clojure.set, but it only works on sets. Why shouldn't it
work on maps? To make select work with maps we just replace disj with
dissoc:

(defn select
"Returns a set of the elements for which pred is true"
[pred xset]
(reduce (fn [s k] (if (pred k) s (dissoc s (key k))))
xset xset))
(def m {:dog 5 :cat 4 :mouse 7 :cow 6})
(select (comp even? val) m)
{:cat 4, :cow 6}

If select was to support maps there seem to be a number of options:
1) allow disj to work on maps as (dissoc s (key %))
2) test if xset is a hashmap and use a different reduce function
3) have separate functions (or namespaces)
4) have a multi-method


Regards,
Tim.

Stuart Sierra

unread,
Sep 1, 2009, 10:11:16 PM9/1/09
to Clojure
On Sep 1, 7:35 pm, Conrad <drc...@gmail.com> wrote:
> Hi everyone! I was wondering if there was a better idiom in Clojure
> for filtering items from a map... Suppose we want to remove all items
> from a map that have an odd number as a value. Here's how I'd write
> it:

I like to use "reduce" for processing maps:

(reduce
(fn [result [key value]]
(if (even? value)
(assoc result key value)
result))
{}
{:dog 5 :cat 4 :mouse 7 :cow 6})

This is slightly more efficient than (into {} ...), although
transients may soon change that.

-SS

Meikel Brandmeyer

unread,
Sep 2, 2009, 1:16:20 AM9/2/09
to clo...@googlegroups.com
Hi,

Am 02.09.2009 um 01:44 schrieb Richard Newman:

> (into {}
> (filter (fn [[key val]]
> (even? val))
> {:dog 5 :cat 4 :mouse 7 :cow 6}))

And to show one more road to Rome (where all roads lead to):

(into {} (for [[k v] the-map :when (even? v)] [k v]))

Sincerely
Meikel

Krukow

unread,
Sep 2, 2009, 11:02:04 AM9/2/09
to Clojure


On Sep 2, 1:44 am, Richard Newman <holyg...@gmail.com> wrote:
> Conrad,
>
>    (into {}
>      (filter (fn [[key val]]
>                (even? val))
>      {:dog 5 :cat 4 :mouse 7 :cow 6}))
>
>    =>
>    {:cat 4, :cow 6}

Or if you like "point-free" style
(into {}
(filter (comp even? second)
{:dog 5 :cat 4 :mouse 7 :cow 6}))
=>
{:cat 4, :cow 6}

/Karl

Krukow

unread,
Sep 2, 2009, 11:03:26 AM9/2/09
to Clojure


On Sep 2, 5:02 pm, Krukow <karl.kru...@gmail.com> wrote:
> Or if you like "point-free" style
> (into {}
>    (filter (comp even? second)
>             {:dog 5 :cat 4 :mouse 7 :cow 6}))
> =>
> {:cat 4, :cow 6}

Saving a few chars, and perhaps more readable:

user> (into {}
(filter (comp even? val)
{:dog 5 :cat 4 :mouse 7 :cow 6}))
{:cat 4, :cow 6}
user>

/K

Sean Devlin

unread,
Sep 2, 2009, 11:39:06 AM9/2/09
to Clojure
If you're gonna go point free...

((comp (partial into {})
(partial filter (comp even? val)))
{:dog 5 :cat 4 :mouse 7 :cow 6})

Reply all
Reply to author
Forward
0 new messages