Kevin
Kevin

Reputation: 25269

clojure filter map by keys

I'm following this example: http://groups.google.com/group/clojure/browse_thread/thread/99b3d792b1d34b56

(see the last reply)

And this is the cryptic error that I get:

Clojure 1.2.1
user=> (def m {:a "x" :b "y" :c "z" :d "w"})
#'user/m
user=> (filter #(some % [:a :b]) m)
java.lang.IllegalArgumentException: Key must be integer
(user=>

Also I don't understand why this would even work. Isn't (some ...) going to return the first matching value, "x", every time? I'm a total noob at clojure and just trying to learn.

Please enlighten me.

Upvotes: 10

Views: 14292

Answers (3)

Joe Lehmann
Joe Lehmann

Reputation: 1085

If you "iterate" over a map, you'll get key-value pairs rather than keys. For instance,

   user=> (map #(str %) {:a 1, :b 2, :c 3})
   ("[:a 1]" "[:b 2]" "[:c 3]")

Thus your anonymous function tries to evaluate (some [:a "x"] [:a :b]) which clearly does not work.

The ideomatic solution is to use select-keys as mentioned in another answer.

Upvotes: 8

isakkarlsson
isakkarlsson

Reputation: 1109

(filter 
  (fn [x] 
    (some #{(key x)} [:a :b])) m)

Would do the same using filter and some (but uglier and slower).

This works by filter all from m if some [:a :b] is in the set #{(key x)} (i.e. using a set as predicate) then return the map entry.

Upvotes: 1

Kevin
Kevin

Reputation: 25269

I guess I just needed to read the docs more:

(select-keys m [:a :b])

Although I'm still not sure what the intention was with the example I found...

Upvotes: 31

Related Questions