Search

July 15, 2020

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.