10

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.

Kevin
  • 24,871
  • 19
  • 102
  • 158
  • The code from the ggroup thread you link to solves a different problem: "given *a collection of* maps and a set of keys, return a collection of precisely those of the given maps which contain at least one of the given keys". Thus, in this code, `filter` is meant to operate on a collection of maps, not a single map; and this particular `#(...)` block involving `some` is only appropriate if the given maps do not contain `nil` or `false` values (as mentioned in the thread), but in any case, the function it returns will be applied (lazily) to each of the given maps in turn. – Michał Marczyk Aug 26 '11 at 21:51
  • ok that explains my misunderstanding, thanks. – Kevin Aug 26 '11 at 23:45

3 Answers3

30

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...

Kevin
  • 24,871
  • 19
  • 102
  • 158
8

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.

Joe Lehmann
  • 1,087
  • 5
  • 10
  • 5
    just a tiny bit of nitpicking: you could say (map str {:a 1 :b 2}) which is slightly more idiomatic. str and #(str %) are functionally equivalent. – Gert Aug 28 '11 at 20:40
1
(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.

isakkarlsson
  • 1,111
  • 9
  • 13