Clojure Goodness: Checking Predicate For Every Or Any Element In A Collection
In Clojure we can use several functions to see if at least one or all elements in a collection return true
or false
for a predicate. The function every?
only returns true
if the predicate function returns true
for all elements in the collection. To function not-every?
return true
if a predicate function return false
for all elements in a collection. The some
function is a bit different (notice there is no ?
) and returns the first logical true
value from the predicate function for the elements in the collection. So the return type of the predicate doesn’t have to be a Boolean
value and then the return type of some
is also not a Boolean
. If the predicate returns a Boolean
value we can use some
like a `any` function (any
is not part of Clojure). Clojure provides a not-any?
function that returns true
if the predicate function returns false
for one element in the collection and false
otherwise.
The following example uses the different functions on a vector with some cartoon names:
(ns mrhaki.seq.pred
(:require [clojure.test :refer [is]]
[clojure.string :as str]))
;; Vector of toons to check predicates on.
(def toons ["Daffy Duck" "Bugs Bunny" "Elmer Fudd" "Yosemite Sam"])
;; Helper function to count number of names.
(defn count-names
[name]
(count (str/split name #" ")))
;; Every toon has two names.
(is (true? (every? #(= 2 (count-names %)) toons)))
;; Not every toon name starts with "A".
(is (true? (not-every? #(str/starts-with? "A" %) toons)))
;; Helper function to check if the first letter
;; of both names is the same.
(defn same-first-letters?
[name]
(let [names (str/split name #" ")
first-letter (first (first names))
second-letter (first (second names))]
(= first-letter second-letter)))
;; Some toons have the same first letter
;; for their first and last name.
(is (true? (some same-first-letters? toons)))
;; Using set as function to check toon is in the toons vector.
;; Notice some function return the first value from the predicate function
;; that is not nil or false, instead of a boolean like with
;; every?, not-every? and not-any?.
(is (not (true? (some #{"Yosemite Sam", "Road Runner"} toons))))
(is (= "Yosemite Sam" (some #{"Yosemite Sam", "Road Runner"} toons)))
;; As seen on https://clojuredocs.org/clojure.core/any_q a
;; possible implementation for any that returns true or false.
(defn any [pred coll] ((comp boolean some) pred coll))
(is (true? (any #{"Yosemite Sam", "Road Runner"} toons)))
;; There is a toon name where their name length is 10.
(is (false? (not-any? #(= (count %) 10) toons)))
Written with Clojure 1.10.1.