Filter map collection by list of keys

3,110 views
Skip to first unread message

Jeff Sigmon

unread,
Jun 12, 2011, 8:01:09 PM6/12/11
to clo...@googlegroups.com
Given the following collection as input

(def input ({:a "a", :b "b"} {:b "b", :c "c"} {:d "d", :e "e"}))

I want to be able to return a subset of the contained maps for each map that contains at least one key from a list of provided keys.

for example:

(filter-keys input [:a :b])
;; out -> ({:a "a", :b "b"} {:b "b", :c "c"}))

(filter-keys input [:e f])
;; out -> ({:d "d", :e "e"})


Here is how I implemented this:


VERSION 1

(defn filter-keys [coll kset]
  (letfn [(keys-found [m]
           (-> m keys set (clojure.set/intersection kset))) 
          (any-keys-found? [m]
            (< 0 (count (keys-found m))))]
    (filter any-keys-found? coll)))


VERSION 2

(defn filter-keys [coll keyseq]
  (letfn [(any-keys-found? [m]
            (< 0 (count (select-keys m keyseq))))]
    (filter any-keys-found? coll)))


VERSION 3 ??

Is there something in clojure core or contrib that would make this easier?


Sean Corfield

unread,
Jun 12, 2011, 8:59:41 PM6/12/11
to clo...@googlegroups.com
On Sun, Jun 12, 2011 at 5:01 PM, Jeff Sigmon <je...@jeffsigmon.com> wrote:
> VERSION 3 ??
> Is there something in clojure core or contrib that would make this easier?

How about:

(defn filter-keys [coll keys] (filter #(some % keys) coll))
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Jeff Sigmon

unread,
Jun 12, 2011, 10:36:59 PM6/12/11
to clo...@googlegroups.com
Wonderful! Pretty cool how the map itself is the predicate to the some function. 

Thanks Sean!

Meikel Brandmeyer

unread,
Jun 13, 2011, 12:05:33 AM6/13/11
to clo...@googlegroups.com
Hi,

Am 13.06.2011 um 04:36 schrieb Jeff Sigmon:

> Wonderful! Pretty cool how the map itself is the predicate to the some function.

The usual disclaimer about keys with false or nil as value applies.

Sincerely
Meikel

Sean Corfield

unread,
Jun 13, 2011, 2:00:05 AM6/13/11
to clo...@googlegroups.com
On Sun, Jun 12, 2011 at 7:36 PM, Jeff Sigmon <je...@jeffsigmon.com> wrote:
> Wonderful! Pretty cool how the map itself is the predicate to the some
> function.

Yes, a map is a function from its keys to its to values, just as a
keyword is also a function (from a map to a value identified by that
keyword).

And, as Meikel notes, if you have a value of false or nil in your map,
my "shortcut" won't work.

If you're on Clojure 1.3.0, a solution that addresses Meikel's concern would be:

(defn filter-keys [coll keys] (filter (apply some-fn (map #(fn [c]
(find c %)) keys)) coll))

Maybe someone can clean that up?

Meikel Brandmeyer

unread,
Jun 13, 2011, 3:02:41 AM6/13/11
to clo...@googlegroups.com
Hi,

Am 13.06.2011 um 08:00 schrieb Sean Corfield:

> (defn filter-keys [coll keys] (filter (apply some-fn (map #(fn [c]
> (find c %)) keys)) coll))
>
> Maybe someone can clean that up?

Just use contains? in your original solution.

(defn filter-keys
[ks coll]
(filter #(some (partial contains? %) ks) coll))

Not as nice as before, but it should work.

Sincerely
Meikel

Sean Corfield

unread,
Jun 13, 2011, 3:19:41 AM6/13/11
to clo...@googlegroups.com
On Mon, Jun 13, 2011 at 12:02 AM, Meikel Brandmeyer <m...@kotka.de> wrote:
> Just use contains? in your original solution.
>
> (defn filter-keys
>  [ks coll]
>  (filter #(some (partial contains? %) ks) coll))

Needs to be (defn filter-keys [coll ks] ...) but, yes, that is cleaner
than mine. Nice.

So many functions...

Meikel Brandmeyer

unread,
Jun 13, 2011, 5:36:47 AM6/13/11
to clo...@googlegroups.com
Hi,

Am 13.06.2011 um 09:19 schrieb Sean Corfield:

>> (defn filter-keys
>> [ks coll]
>> (filter #(some (partial contains? %) ks) coll))
>
> Needs to be (defn filter-keys [coll ks] ...)

I disagree. Clojure puts sequence arguments usually last. See filter, remove, take-while, ... I would exchange the arguments and follow the usual Clojure style.

> So many functions...

Hehe. :) That's why it's called “functional.” ;)

Sincerely
Meikel

Sean Corfield

unread,
Jun 13, 2011, 12:50:31 PM6/13/11
to clo...@googlegroups.com
On Mon, Jun 13, 2011 at 2:36 AM, Meikel Brandmeyer <m...@kotka.de> wrote:
>> Needs to be (defn filter-keys [coll ks] ...)
> I disagree. Clojure puts sequence arguments usually last. See filter, remove, take-while, ... I would exchange the arguments and follow the usual Clojure style.

The OP specifically asked for (filter-keys input [:a :b]) - I was
merely pointing out your solution had the arguments the wrong way
round _for his question_.

I do agree that the usual practice is to have the collection last and
actually kept doing that while I was trying to solve his puzzle - and
then I'd run his example and it wouldn't work, until I swapped the
arguments :)

Jonathan Fischer Friberg

unread,
Jun 13, 2011, 7:30:38 PM6/13/11
to clo...@googlegroups.com
I find that functions that extract or update data from some collection often has the collection first.
nth, assoc, contains?, get-in, assoc-in, update-in ...

Jonathan

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply all
Reply to author
Forward
0 new messages