Tuesday, August 23, 2011

Clojure: Check For nil In a List

The every? function in Clojure is very helpful for determining if every element of a list passes a predicate. From the docs:
Usage: (every? pred coll)

Returns true if (pred x) is logical true for every x in coll, else false.
The usage of every? is very straightforward, but a quick REPL session is always nice to verify our assumptions.
Clojure 1.2.0

user=> (every? nil? [nil nil nil])
true
user=> (every? nil? [nil 1])
false
As expected, every? works well when you know exactly what predicate you need to use.

Yesterday, I was working on some code that included checking for nil - similar to the example below.
user=> (def front 1)

#'user/front
user=> (def back 2)
#'user/back
user=> (when (and front back) [front back])
[1 2]
This code works perfectly if you have the individual elements "front" and "back", but as the code evolved I ended up representing "front" and "back" simply as a list of elements. Changing to a list required a way to verify that each entry in the list was not nil.

I was 99% sure that "and" was a macro; therefore, combining it with apply wasn't an option. A quick REPL reference verified my suspicion.
user=> (def legs [front back])               

#'user/legs
user=> (when (apply and legs) legs)
java.lang.Exception: Can't take value of a macro: #'clojure.core/and (NO_SOURCE_FILE:8)
Several other options came to mind, an anonymous function that checked for (not (nil? %)), map the values to (not (nil? %)) and use every? with true?; however, because of Clojure's truthiness the identity function is really all you need. The following REPL session shows how identity works perfectly as our predicate for this example.
(when (every? identity legs) legs)

[1 2]
For a few more looks at behavior, here's a few examples that include nil and false.
user=> (every? identity [1 2 3 4])

true
user=> (every? identity [1 2 nil 4])
false
user=> (every? identity [1 false 4])
false
As you can see, using identity will cause every? to fail if any element is falsey (nil or false). In my case the elements are integers or nil, so this works perfectly; however, it's worth noting so you don't see unexpected results if booleans ever end up in your list.

5 comments:

  1. This is what I use to check if a coll only contains non-nil values:

    user> (not (some nil? [1 2 3 4]))
    true
    user> (not (some nil? [1 2 nil 4]))
    false
    user> (not (some nil? [1 false 4]))
    true

    ReplyDelete
  2. @Jürgen
    I'd probably do the same if I needed "false" to be a valid value. Thanks for the comment.

    ReplyDelete
  3. Another option is:

    user> (not-any? nil? [1 2 3 4])
    true

    ReplyDelete
  4. @Jesse, indeed. I always forget not-any? since any? doesn't exist. Thanks for pointing that out.

    I wonder which is more readable? I can't say I'm a huge fan of either, tho I think not-any? nil? gets a slight nod.

    (none? nil? coll) would probably be a bit easier... *shrug*

    ReplyDelete
  5. Quite nice solution. I'd use complement myself.

    (every? (complement nil?) seq)

    Maybe even go so far as define a new function not-nil? In order to make the code express my intention.

    ReplyDelete

Note: Only a member of this blog may post a comment.