198

What is the best way to test whether a list contains a given value in Clojure?

In particular, the behaviour of contains? is currently confusing me:

(contains? '(100 101 102) 101) => false

I could obviously write a simple function to traverse the list and test for equality, but there must surely be a standard way to do this?

Micah Elliott
10.4k5 gold badges57 silver badges56 bronze badges
asked Jul 14, 2010 at 18:42
3
  • 7
    Strange indeed, contains? has to be the most misleadingly named function in Clojure :) Here's hoping that Clojure 1.3 will see it renamed to contains-key? or similar. Commented Jul 14, 2010 at 19:05
  • 4
    I think this is talked to death several times now. contains? will not change. See here: groups.google.com/group/clojure/msg/f2585c149cd0465d and groups.google.com/group/clojure/msg/985478420223ecdf Commented Jul 15, 2010 at 11:55
  • 1
    @kotarak thanks for the link! I actually agree with Rich here in terms of the use of the contains? name though I think it should be altered to throw an error when applied to a list or sequence Commented Jul 15, 2010 at 13:42

19 Answers 19

240

Ah, contains?... supposedly one of the top five FAQs re: Clojure.

It does not check whether a collection contains a value; it checks whether an item could be retrieved with get or, in other words, whether a collection contains a key. This makes sense for sets (which can be thought of as making no distinction between keys and values), maps (so (contains? {:foo 1} :foo) is true) and vectors (but note that (contains? [:foo :bar] 0) is true, because the keys here are indices and the vector in question does "contain" the index 0!).

(削除) To add to the confusion, in cases where it doesn't make sense to call contains?, it simply return false; this is what happens in (contains? :foo 1) and also (contains? '(100 101 102) 101). (削除ここまで) Update: In Clojure ≥ 1.5 contains? throws when handed an object of a type that doesn't support the intended "key membership" test.

The correct way to do what you're trying to do is as follows:

; most of the time this works
(some #{101} '(100 101 102))

When searching for one of a bunch of items, you can use a larger set; when searching for false / nil, you can use false? / nil? -- because (#{x} x) returns x, thus (#{nil} nil) is nil; when searching for one of multiple items some of which may be false or nil, you can use

(some (zipmap [...the items...] (repeat true)) the-collection)

(Note that the items can be passed to zipmap in any type of collection.)

answered Jul 14, 2010 at 18:49
Sign up to request clarification or add additional context in comments.

8 Comments

As Michal said - there is already a function in core which does what you desire: some.
Above, Michal commented about (some #{101} '(100 101 102)) saying that "most of the time this works". Isn't it fair to say that it always works? I'm using Clojure 1.4 and the documentation uses this kind of example. It works for my and makes sense. Is there some kind of special case where it does not work?
@DavidJames: It doesn't work if you're checking for the presence of false or nil -- see the following paragraph. On a separate note, in Clojure 1.5-RC1 contains? throws an exception when given a non-keyed collection as an argument. I suppose I'll edit this answer when the final release comes out.
This is stupid! The main distinction of a collection is the membership relation. It should had been the most important function for collections. en.wikipedia.org/wiki/Set_(mathematics)#Membership
@jgomo3 You can use contains? on a set to test for membership. What you can't do is use it on a map or a list, because doing so is no longer O(1), but becomes O(n). contains? is meant to be O(1), which is why you need to do your own linear search in those cases.
|
163

Here's my standard util for the same purpose:

(defn in? 
 "true if coll contains elm"
 [coll elm] 
 (some #(= elm %) coll))
nha
18.1k15 gold badges95 silver badges141 bronze badges
answered Jul 14, 2010 at 19:34

7 Comments

This is the simplest and safest solution, as it also handles falsy values like nil and false. Now why is this not part of clojure/core?
seq could maybe be renamed to coll, to avoid confusion with the function seq ?
@nha You could do that, yes. It doesn't matter here: Since we're not using the function seq inside the body, there's no conflict with the parameter of the same name. But feel free to edit the answer if you think the renaming would make it easier to understand.
It's worth noting that this can 3-4x slower than (boolean (some #{elm} coll)) if you don't have to worry about nil or false.
@AviFlax I was thinking about clojure.org/guides/threading_macros, where it says "By convention, core functions that operate on sequences expect the sequence as their last argument. Accordingly, pipelines containing map, filter, remove, reduce, into, etc usually call for the ->> macro." But I guess the convention is more about functions that operate on sequences and return sequences.
|
34

You can always call java methods with .methodName syntax.

(.contains [100 101 102] 101) => true
answered Aug 19, 2015 at 16:51

2 Comments

IMHO this is the best answer. Too bad clojure contains? is so confusingly named.
The venerable master Qc Na was walking with his student, Anton. When Anton told him about having some beginner's problem with contains?, Qc Na hit him with a Bô and said: "Stupid student! You have to realize there is no spoon. It's all just Java underneath! Use the dot notation.". At that moment, Anton became enlightened.
18

I know that I'm a little bit late, but what about:

(contains? (set '(101 102 103)) 102)

At last in clojure 1.4 outputs true :)

Aliaksandr Sushkevich
12.6k8 gold badges41 silver badges46 bronze badges
answered Sep 19, 2012 at 3:01

2 Comments

(set '(101 102 103)) is the same as %{101 102 103}. So your answer can be written as (contains? #{101 102 103} 102).
This has the disadvantage of requiring the conversion of the original list '(101 102 103) to a set.
16
(not= -1 (.indexOf '(101 102 103) 102))

Works, but below is better:

(some #(= 102 %) '(101 102 103)) 
Felipe Augusto
8,22413 gold badges43 silver badges76 bronze badges
answered Oct 10, 2012 at 13:12

1 Comment

some returns nil if nothing matches, not false
7

Here's a quick function out of my standard utilities that I use for this purpose:

(defn seq-contains?
 "Determine whether a sequence contains a given item"
 [sequence item]
 (if (empty? sequence)
 false
 (reduce #(or %1 %2) (map #(= %1 item) sequence))))
answered Jul 14, 2010 at 19:19

1 Comment

Yeah, yours has the advantage that it will stop as soon as it finds a match rather than continuing to map the entire sequence.
7

If you have a vector or list and want to check whether a value is contained in it, you will find that contains? does not work. Michał has already explained why.

; does not work as you might expect
(contains? [:a :b :c] :b) ; = false

There are four things you can try in this case:

  1. Consider whether you really need a vector or list. If you use a set instead, contains? will work.

    (contains? #{:a :b :c} :b) ; = true
    
  2. Use some , wrapping the target in a set, as follows:

    (some #{:b} [:a :b :c]) ; = :b, which is truthy
    
  3. The set-as-function shortcut will not work if you are searching for a falsy value (false or nil).

    ; will not work
    (some #{false} [true false true]) ; = nil
    

    In these cases, you should use the built-in predicate function for that value, false? or nil?:

    (some false? [true false true]) ; = true
    
  4. If you will need to do this kind of search a lot, write a function for it:

    (defn seq-contains? [coll target] (some #(= target %) coll))
    (seq-contains? [true false true] false) ; = true
    

Also, see Michał’s answer for ways to check whether any of multiple targets are contained in a sequence.

answered Nov 24, 2013 at 12:30

Comments

6

For what it is worth, this is my simple implementation of a contains function for lists:

(defn list-contains? [coll value]
 (let [s (seq coll)]
 (if s
 (if (= (first s) value) true (recur (rest s) value))
 false)))
answered Jul 14, 2010 at 19:21

1 Comment

Can we ask for the predicate part as an argument ? To get something like : (defn list-contains? [pred coll value] (let [s (seq coll)] (if s (if (pred (first s) value) true (recur (rest s) value)) false)))
5

Here's the classic Lisp solution:

(defn member? [list elt]
 "True if list contains at least one instance of elt"
 (cond 
 (empty? list) false
 (= (first list) elt) true
 true (recur (rest list) elt)))
Sergey Tselovalnikov
6,0562 gold badges39 silver badges60 bronze badges
answered Apr 5, 2014 at 11:34

1 Comment

OK,the reason that is a poor solution in Clojure is that it recurses up the stack on one processor. A better Clojure solution is <pre> (defn member? [elt col] (some #(= elt %) col)) </pre> This is because some is potentially parallel across available cores.
4

I've built upon j-g-faustus version of "list-contains?". It now takes any number of arguments.

(defn list-contains?
([collection value]
 (let [sequence (seq collection)]
 (if sequence (some #(= value %) sequence))))
([collection value & next]
 (if (list-contains? collection value) (apply list-contains? collection next))))
answered Oct 6, 2011 at 11:29

Comments

3

It is as simple as using a set - similar to maps, you can just drop it in the function position. It evaluates to the value if in the set (which is truthy) or nil (which is falsey):

(#{100 101 102} 101) ; 101
(#{100 101 102} 99) ; nil

If you're checking against a reasonably sized vector/list you won't have until runtime, you can also use the set function:

; (def nums '(100 101 102))
((set nums) 101) ; 101
answered Apr 28, 2015 at 17:32

Comments

1

The recommended way is to use some with a set - see documentation for clojure.core/some.

You could then use some within a real true/false predicate, e.g.

(defn in? [coll x] (if (some #{x} coll) true false))
answered Mar 16, 2013 at 17:04

2 Comments

why the if true and false? some already returns true-ish and false-ish values.
what about (some #{nil} [nil]) ? It would return nil which will be converted to false.
1
(defn in?
 [needle coll]
 (when (seq coll)
 (or (= needle (first coll))
 (recur needle (next coll)))))
(defn first-index
 [needle coll]
 (loop [index 0
 needle needle
 coll coll]
 (when (seq coll)
 (if (= needle (first coll))
 index
 (recur (inc index) needle (next coll))))))
answered Jul 15, 2013 at 21:20

Comments

1
(defn which?
 "Checks if any of elements is included in coll and says which one
 was found as first. Coll can be map, list, vector and set"
 [ coll & rest ]
 (let [ncoll (if (map? coll) (keys coll) coll)]
 (reduce
 #(or %1 (first (filter (fn[a] (= a %2))
 ncoll))) nil rest )))

example usage (which? [ 1 2 3 ] 3) or (which? #{ 1 2 3} 4 5 3)

answered Jun 18, 2016 at 23:04

1 Comment

still no language-core supplied function for it?
1

Since Clojure is built on Java, you can just as easily call the .indexOf Java function. This function returns the index of any element in a collection, and if it can't find this element, returns -1.

Making use of this we could simply say:

(not= (.indexOf [1 2 3 4] 3) -1)
=> true
Felipe Augusto
8,22413 gold badges43 silver badges76 bronze badges
answered Jan 27, 2016 at 15:30

Comments

1

Another option:

((set '(100 101 102)) 101)

Use java.util.Collection#contains():

(.contains '(100 101 102) 101)
answered Mar 21, 2020 at 14:38

Comments

0

The problem with the 'recommended' solution is it is breaks when the value you are seeking is 'nil'. I prefer this solution:

(defn member?
 "I'm still amazed that Clojure does not provide a simple member function.
 Returns true if `item` is a member of `series`, else nil."
 [item series]
 (and (some #(= item %) series) true))
answered Apr 29, 2016 at 10:31

Comments

0

There are convenient functions for this purpose in the Tupelo library. In particular, the functions contains-elem?, contains-key?, and contains-val? are very useful. Full documentation is present in the API docs.

contains-elem? is the most generic and is intended for vectors or any other clojure seq:

 (testing "vecs"
 (let [coll (range 3)]
 (isnt (contains-elem? coll -1))
 (is (contains-elem? coll 0))
 (is (contains-elem? coll 1))
 (is (contains-elem? coll 2))
 (isnt (contains-elem? coll 3))
 (isnt (contains-elem? coll nil)))
 (let [coll [ 1 :two "three" 4円]]
 (isnt (contains-elem? coll :no-way))
 (isnt (contains-elem? coll nil))
 (is (contains-elem? coll 1))
 (is (contains-elem? coll :two))
 (is (contains-elem? coll "three"))
 (is (contains-elem? coll 4円)))
 (let [coll [:yes nil 3]]
 (isnt (contains-elem? coll :no-way))
 (is (contains-elem? coll :yes))
 (is (contains-elem? coll nil))))

Here we see that for an integer range or a mixed vector, contains-elem? works as expected for both existing and non-existant elements in the collection. For maps, we can also search for any key-value pair (expressed as a len-2 vector):

 (testing "maps"
 (let [coll {1 :two "three" 4円}]
 (isnt (contains-elem? coll nil ))
 (isnt (contains-elem? coll [1 :no-way] ))
 (is (contains-elem? coll [1 :two]))
 (is (contains-elem? coll ["three" 4円])))
 (let [coll {1 nil "three" 4円}]
 (isnt (contains-elem? coll [nil 1] ))
 (is (contains-elem? coll [1 nil] )))
 (let [coll {nil 2 "three" 4円}]
 (isnt (contains-elem? coll [1 nil] ))
 (is (contains-elem? coll [nil 2] ))))

It is also straightforward to search a set:

 (testing "sets"
 (let [coll #{1 :two "three" 4円}]
 (isnt (contains-elem? coll :no-way))
 (is (contains-elem? coll 1))
 (is (contains-elem? coll :two))
 (is (contains-elem? coll "three"))
 (is (contains-elem? coll 4円)))
 (let [coll #{:yes nil}]
 (isnt (contains-elem? coll :no-way))
 (is (contains-elem? coll :yes))
 (is (contains-elem? coll nil)))))

For maps & sets, it is simpler (& more efficient) to use contains-key? to find a map entry or a set element:

(deftest t-contains-key?
 (is (contains-key? {:a 1 :b 2} :a))
 (is (contains-key? {:a 1 :b 2} :b))
 (isnt (contains-key? {:a 1 :b 2} :x))
 (isnt (contains-key? {:a 1 :b 2} :c))
 (isnt (contains-key? {:a 1 :b 2} 1))
 (isnt (contains-key? {:a 1 :b 2} 2))
 (is (contains-key? {:a 1 nil 2} nil))
 (isnt (contains-key? {:a 1 :b nil} nil))
 (isnt (contains-key? {:a 1 :b 2} nil))
 (is (contains-key? #{:a 1 :b 2} :a))
 (is (contains-key? #{:a 1 :b 2} :b))
 (is (contains-key? #{:a 1 :b 2} 1))
 (is (contains-key? #{:a 1 :b 2} 2))
 (isnt (contains-key? #{:a 1 :b 2} :x))
 (isnt (contains-key? #{:a 1 :b 2} :c))
 (is (contains-key? #{:a 5 nil "hello"} nil))
 (isnt (contains-key? #{:a 5 :doh! "hello"} nil))
 (throws? (contains-key? [:a 1 :b 2] :a))
 (throws? (contains-key? [:a 1 :b 2] 1)))

And, for maps, you can also search for values with contains-val?:

(deftest t-contains-val?
 (is (contains-val? {:a 1 :b 2} 1))
 (is (contains-val? {:a 1 :b 2} 2))
 (isnt (contains-val? {:a 1 :b 2} 0))
 (isnt (contains-val? {:a 1 :b 2} 3))
 (isnt (contains-val? {:a 1 :b 2} :a))
 (isnt (contains-val? {:a 1 :b 2} :b))
 (is (contains-val? {:a 1 :b nil} nil))
 (isnt (contains-val? {:a 1 nil 2} nil))
 (isnt (contains-val? {:a 1 :b 2} nil))
 (throws? (contains-val? [:a 1 :b 2] 1))
 (throws? (contains-val? #{:a 1 :b 2} 1)))

As seen in the test, each of these functions works correctly when for searching for nil values.

answered Feb 17, 2017 at 21:38

Comments

0

Found this late. But this is what im doing

(some (partial = 102) '(101 102 103)) 
answered Jan 26, 2022 at 19:22

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.