Pure Danger Tech


navigation
home

Learning Clojure #2: contains?

08 Feb 2010

I spent 30 minutes today learning that contains? has surprising (to me at least) behavior on indexed collections. I expected it to tell me whether the vector contains an item and indeed this is how it works on collections like lists, sets, maps, etc.

For example:

user=> (contains? #{ "a" "b"} "a")
true
user=> (contains? { :a 1 :b 2 } :a)
true
user=> (contains? ["a" "b" ] "a")
false

What? Check the docs:

user=> (doc contains?)
-------------------------
clojure.core/contains?
([coll key])
  Returns true if key is present in the given collection, otherwise
  returns false.  Note that for numerically indexed collections like
  vectors and Java arrays, this tests if the numeric key is within the
  range of indexes. 'contains?' operates constant or logarithmic time;
  it will not perform a linear search for a value.  See also 'some'.

So for structures like vectors and arrays, the contains? function expects a numerical argument that is an index, not an item.

user=> (contains? ["a" "b"] 0)
true
user=> (contains? ["a" "b"] 1)
true
user=> (contains? ["a" "b"] 2)
false

I don’t have a better solution at hand for how to do this with vectors and arrays. I’d love to see one.

Update from @fogus:

user=> (some #{"b"} ["a" "b"])
"b"
user=> (some #{"d"} ["a" "b"])
nil

For the purposes of logical true/false, the “b” will evaluate to true. I’ll just say that’s pretty obscure in my book.

Update #2: Some useful info here too on this subject. Thanks @wmacgyver.