Pure Danger Tech


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")
user=> (contains? { :a 1 :b 2 } :a)
user=> (contains? ["a" "b" ] "a")

What? Check the docs:

user=> (doc 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)
user=> (contains? ["a" "b"] 1)
user=> (contains? ["a" "b"] 2)

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"])
user=> (some #{"d"} ["a" "b"])

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.