Test whether a list contains a specific value in Clojure

Data StructuresClojure

Data Structures Problem Overview


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?

Data Structures Solutions


Solution 1 - Data Structures

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

Solution 2 - Data Structures

Here's my standard util for the same purpose:

(defn in? 
  "true if coll contains elm"
  [coll elm]  
  (some #(= elm %) coll))

Solution 3 - Data Structures

You can always call java methods with .methodName syntax.

(.contains [100 101 102] 101) => true

Solution 4 - Data Structures

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 :)

Solution 5 - Data Structures

(not= -1 (.indexOf '(101 102 103) 102))

Works, but below is better:

(some #(= 102 %) '(101 102 103)) 

Solution 6 - Data Structures

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

Solution 7 - Data Structures

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.

Solution 8 - Data Structures

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

Solution 9 - Data Structures

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

Solution 10 - Data Structures

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

Solution 11 - Data Structures

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

Solution 12 - Data Structures

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

Solution 13 - Data Structures

(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))))))

Solution 14 - Data Structures

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

Solution 15 - Data Structures

(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)

Solution 16 - Data Structures

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

Solution 17 - Data Structures

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.

Solution 18 - Data Structures

Another option:

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

Use java.util.Collection#contains():

(.contains '(100 101 102) 101)

Solution 19 - Data Structures

Found this late. But this is what im doing

(some (partial = 102) '(101 102 103)) 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmikeraView Question on Stackoverflow
Solution 1 - Data StructuresMichał MarczykView Answer on Stackoverflow
Solution 2 - Data Structuresj-g-faustusView Answer on Stackoverflow
Solution 3 - Data StructuresYury LitvinovView Answer on Stackoverflow
Solution 4 - Data StructuresGiuliani SanchesView Answer on Stackoverflow
Solution 5 - Data StructuresjamesqiuView Answer on Stackoverflow
Solution 6 - Data StructuresmikeraView Answer on Stackoverflow
Solution 7 - Data StructuresRory O'KaneView Answer on Stackoverflow
Solution 8 - Data StructuresG__View Answer on Stackoverflow
Solution 9 - Data StructuresSimon BrookeView Answer on Stackoverflow
Solution 10 - Data StructuresUrs ReupkeView Answer on Stackoverflow
Solution 11 - Data StructuresBrad KochView Answer on Stackoverflow
Solution 12 - Data StructuresKingCodeView Answer on Stackoverflow
Solution 13 - Data StructuresDavidView Answer on Stackoverflow
Solution 14 - Data StructuresAStantonView Answer on Stackoverflow
Solution 15 - Data StructuresMichaelView Answer on Stackoverflow
Solution 16 - Data StructuresSimon BrookeView Answer on Stackoverflow
Solution 17 - Data StructuresAlan ThompsonView Answer on Stackoverflow
Solution 18 - Data StructuresAlexView Answer on Stackoverflow
Solution 19 - Data StructuresjstuartmilneView Answer on Stackoverflow