How to Check if A List Contains a Value in Clojure
This article goes over four different methods for determining whether a list has a specific value using Clojure.
Join the DZone community and get the full member experience.
Join For FreeRecently I started to learn Clojure, and usually, my first phase is to complete small programming exercises. To practice Clojure I decided to solve some CodingBat problems. A common task here is to check, if an array does contain some value. The expected result should be a boolean value. When I moved to practical implementations, I realized how tricky can be a simple thing in Clojure.
In this post, I would like to share with you some thoughts on how to check, that a list contains an element in Clojure. As almost any beginner I have started with contains?
method, however, it may not be your best option. I will explain to you several solutions, including ones, that come from Java.
What Is Wrong with Contains? Function?
This is an expected question. To explain it, let me take a simple exercise from CodingBat. For example, array-1/has23. The problem statement asks us to return a boolean value, which reflects if the given array contains 2 or 3. Pretty simple, isn’t it? Let start with contains?
function. Take a look at the following code snippet below:
x
(defn has23 [numbers]
(or (contains? numbers 2) (contains? numbers 3))
)
(println (has23 [2 5]))
(println (has23 [4 5]))
(println (has23 [1 3]))
What is wrong with this code? When you will execute it you will receive an output, similar to this:
false false false
But there are values – the first list has 2 and the last one has 3, why it is false? The answer is in the purpose of this method. contains?
returns a boolean value, which represents if a data structure contains a key. So, it works for associative data structures (like maps). For lists a key is an index of an element. Because we have an array of 2 elements, it can’t have an index 2 and moreover 3! As a result, this approach would not work. Hopefully, there are others.
Using some
When you will search for the solution, most probably you will find recommendations to use some
. It works fine with sequential data structures, like lists. This function takes two arguments: a predicate (logical condition) and a data structure itself. The return is a first matching element.
Let try it with an another exercise – warmup-2/arrayFront9. The task here demands us to return true, if there is 9 in first 4 elements. Note, that I use take
function to get first 4 elements:
x
(defn arrayFront9 [numbers]
(some #{9} (take 4 numbers))
)
(println (arrayFront9 [1 2 9 3 4]))
The output of the code listing above is 9
.
Now we will check result with a list, which does not have 9 among first four elements:
x
(defn arrayFront9 [numbers]
(some #{9} (take 4 numbers))
)
(println (arrayFront9 [1 2 3 4 9]))
In this case, the output is nil
. This may lead to an error, if another logic relies on this function, which can return a null value. Furthermore, we need to get a boolean result. Let refactor our solution:
x
(defn arrayFront9 [numbers]
(not= (some #{9} (take 4 numbers)) nil)
)
(println (arrayFront9 [1 2 9 3 4]))
(println (arrayFront9 [1 2 3 4 9]))
I added an assertion to check if the element is not null. In other words, if 9 does exist among first sub sequence, it will be returned, otherwise the result will be null. So, I wrapped it around logical validation to return a boolean value. The output of this code is:
xxxxxxxxxx
true false
As expected!
Go Java
In my previous post on Clojure exception handling, I stated that as Clojure runs on JVM it inherits a lot of from Java. That leads us to an another solution – using Java methods to work with collections. If you have read my article on Java collections, you may remember that we have two methods in our disposal:
contains
= returns true if an element is presented in a collectionindexOf
= returns an index of an element, or -1 in case of its absence
In this section we will observe, how to use them both.
Contains
Let take the exercise called array-1/no23. It is a mirror to what we have already solved in has23
, but now we need to assure, that the list does not contain 2 or 3. In order to call Java methods in Clojure we utilize a concept of dot notation:
x
(defn no23 [numbers]
(not (or (.contains numbers 2) (.contains numbers 3)))
)
(println (no23 [5 4]))
(println (no23 [2 1]))
(println (no23 [4 3]))
When you will run this code snippet, you will receive an output like that:
xxxxxxxxxx
true false false
Remember, that we are expected to find an absence of 2 and 3 in a list. This means, we need to negate a result of calling .contains
function.
IndexOf
While looking for a solution, I have found an interesting approach. It is based on using indexOf
method. As you remember, this function checks for an element and returns its index or -1 (when an element is not in a list).
To illustrate this point let take again arrayFront9
exercise, but not we will refactor it to use indexOf
. Take a look on my implementation below:
x
(defn arrayFront9IndexOf [numbers]
(not= (.indexOf (take 4 numbers) 9) -1)
)
(println (arrayFront9IndexOf [1 2 9 3 4]))
(println (arrayFront9IndexOf [1 2 3 4 9]))
The result of an execution will be following:
xxxxxxxxxx
true false
We use negation not=
in a similar way, like with some
. We need to validate, that a result from calling indexOf
function is not equal to -1 (that means an absence of 9). Any other value corresponds to a presence.
Published at DZone with permission of Yuri Mednikov. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments