Recently 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 on the following code snippet below:
(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:
(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:
(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:
(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 subsequence, 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:
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 soltuion - 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 collection
- indexOf = 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 in a has23 example, 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:
(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:
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:
(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:
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 absense of 9). Any other value corresponds to a presence.
That is all for this post. Hope, that it helped you in a some manner. If you have questions regarding a topic of how to check if a list contains a value in Clojure, you can ask them in comments below or contact me.
Top comments (0)