Clojure Goodness: Taking Or Dropping Elements From A Collection Based On Predicate
In Clojure we can take or drop elements from a collection based on a predicate using the functions take-while
and drop-while
. With the function take-while
we take elements as long as the predicate returns true
. Once the predicate returns false
the function stops returning elements. Using the function drop-while
we skip elements in the collection if the predicate returns true
. If the predicate returns false
the remaining elements in the collection are returned.
In the following example we use take-while
and drop-while
with different collection types:
(ns mrhaki.seq.take-while
(:require [clojure.test :refer [is]]
[clojure.string :refer [join]]))
;; Simple range of numbers to 10 to invoke
;; take-while and drop-while functions.
(def numbers (range 10))
;; Use take-while to get all number as long as
;; the number is less than 5.
(is (= [0 1 2 3 4] (take-while #(< % 5) numbers)))
;; Use drop-while to skip all numbers that are
;; less than 5, so we get all numbers from 5.
(is (= [5 6 7 8 9] (drop-while #(< % 5) numbers)))
;; String is a collection of characters so
;; we can use take-while and drop-while.
(def s "Clojure Rocks!")
(is (= "Clojure "
(join (take-while #(not= \R %) s))))
(is (= "Rocks!"
(join (drop-while #(not= \R %) s))))
;; A map structure is a collection of key/value vectors,
;; so take-while and drop-while can be used.
(def user {:name "mrhaki" :loves "Clojure" :worksAt "JDriven"})
;; Helper function to return the length of a keyword.
(defn keyword-length
"Returns length of keyword."
[entry]
(.length (name (key entry))))
(is (= {:name "mrhaki"}
(into {} (take-while #(= 4 (keyword-length %)) user))))
(is (= {:loves "Clojure" :worksAt "JDriven"}
(into {} (drop-while #(= (key %) :name) user))))
Written with Clojure 1.10.1.