Clojure Goodness Notebook
Clojure Goodness Notebook
Hubert A. Klein Ikkink (mrhaki)
Buy on Leanpub

Table of Contents

About Me

I am born in 1973 and live in Tilburg, the Netherlands, with my three gorgeous children. I am also known as mrhaki, which is simply the initials of his name prepended by mr. The following Clojure snippet shows how the alias comes together:

(assert
  (= "mrhaki"
    (reduce
      (fn [nickname name] (str nickname (clojure.string/lower-case (first name))))
          "mr"
          ["Hubert" "Alexander" "Klein" "Ikkink"])))))

(How cool is Clojure that we can express this in a simple code sample ;-) )

I studied Information Systems and Management at the Tilburg University. After finishing my studies I started to work at a company which specialized in knowledge-based software. There I started writing my first Java software (yes, an applet!) in 1996. Over the years my focus switched from applets, to servlets, to Java Enterprise Edition applications, to Spring-based software.

In 2008 I wanted to have fun again when writing software. The larger projects I was working on were more about writing configuration XML files, tuning performance and less about real development. So I started to look around and noticed Groovy as a good language to learn about. I could still use existing Java code, libraries, and my Groovy classes in Java. The learning curve isn’t steep and to support my learning phase I wrote down interesting Groovy facts in my blog with the title Groovy Goodness. I post small articles with a lot of code samples to understand how to use Groovy. Since November 2011 I am also a DZone Most Valuable Blogger (MVB); DZone also posts my blog items on their site. After writing about Groovy I noticed that writing about a subject while learning the subject is very useful. So it became an habit to start writing small blog posts when I learned something new.

I have spoken at the Gr8Conf Europe and US editions about Groovy, Gradle, Grails and Asciidoctor topics. Other conferences where I talked are Greach in Madrid, Spain, JavaLand in Germany and JFall in The Netherlands.

I work for a company called JDriven in the Netherlands. JDriven focuses on technologies that simplify and improve development of enterprise applications. Employees of JDriven have years of experience with Java and related technologies and are all eager to learn about new technologies.

Introduction

When I started to learn about Clojure I wrote done little code snippets with features of Clojure I found interesting. To access my notes from different locations I wrote the snippets with a short explanation in a blog: Messages from mrhaki. I labeled the post as Clojure Goodness, because I thought Clojure is really nice and elegant, and that is how the Clojure Goodness series began.

A while ago I bundled all my blog Groovy Goodness blog posts in a book published at Leanpub. Leanpub is very easy to use and I could use Markdown to write the content, which I really liked as a developer. So it felt natural to also bundle the Clojure Goodness blog posts in a book and publish at Leanpub.

In this book the blog posts are bundled and categorized into sections. Within each section blog posts that cover similar features are grouped. The book is intended to browse through the subjects. You should be able to just open the book at a random page and learn more about Clojure Maybe pick it up once in a while and learn a bit more about known and lesser known features of Clojure

I hope you will enjoy reading the book and it will help you with learning about Clojure, so you can apply all the goodness in your projects.

Core

There Is Truth In Many Things

In Clojure there are only two things false in a boolean or conditional context: false and nil. All other values are threated as true. A value that is false in a boolean context is also called falsey, and a value that is true is called truthy.

In the following example we use false and nil as falsey and several other values as truthy values:

(ns mrhaki.core.truthy-falsey
  (:require [clojure.test :refer [is]]))

;; Only nil and false are falsey.
(is (= false (boolean false)))
(is (= false (boolean nil)))
(is (= false (boolean ({:cool "Clojure"} :language))))

(is (= :falsey (if nil :truthy :falsey)))

;; Other values are truthy.
(is (= true (boolean true)))
(is (= true (boolean "mrhaki")))
(is (= true (boolean 0)))
(is (= true (boolean 1)))
(is (= true (boolean '())))
(is (= true (boolean [1 2 3])))
(is (= true (boolean {})))
(is (= true (boolean ({:rocks "Clojure"} :rocks))))

(is (= :truthy (if "Clojure" :truthy :falsey)))
(is (= :truthy (if "" :truthy :falsey)))
(is (= :empty-but-truthy (if [] :empty-but-truthy :empty-and-falsey)))

Written with Clojure 1.10.1.

Original post written on October 5, 2020

Get Clojure Version

To get the current Clojure version we must use the clojure-version function. The function simply returns the Clojure version we are using from our code.

In the following example we simply check the result of clojure-version and also define a function to get the Javaa version:

(ns mrhaki.core.version
  (:require [clojure.test :refer [is]]))

;; Function clojure-version returns Clojure version.
(is (= "1.10.1" (clojure-version)))

(defn java-version
  "Returns Java version as printable string."
  []
  (System/getProperty "java.version"))

(is (= "14" (java-version)))

Written with Clojure 1.10.1.

Original post written on April 6, 2020

Keyword As Function

In Clojure functions are everywhere. In a previous post we learned that sets can be functions, but Clojure also makes keywords functions. A keyword is a symbol starting with a colon (:) and is mostly used in map entries as key symbol. The keyword as function accepts a map as single argument and returns the value for the key that equals the keyword in the map or nil if the keyword cannot be found.

In the following code we use keywords as function in several examples:

(ns mrhaki.core.keyword-function
  (:require [clojure.test :refer [is]]))

;; Sample map to use in examples.
(def user {:name "Hubert" :alias "mrhaki" :living {:country "Netherlands"}})


;; Keyword is a function with a map argument and
;; returns value for keyword in the map.
(is (= "mrhaki" 
       (:alias user)
       ;; We get the same result with get.
       (get user :alias)))

(is (= {:country "Netherlands"} (:living user)))

(is (= "Netherlands"
       (:country (:living user))
       (-> user :living :country)
       ;; We can use get-in to get values from nested maps.
       (get-in user [:living :country])))


;; When keyword is not in the map we get a nil result.
(is (nil? (:city user)))
(is (= "not-found" (or (:city user) "not-found")))


;; Works also for namespaced keywords.
(is (= "mrhaki" (:user/alias {:name/full-name "Hubert" :user/alias "mrhaki"})))


;; Using keyword as function with juxt.
(is (= ["mrhaki" "Hubert"] 
       ((juxt :alias :name) user)))

Written with Clojure 1.10.1.

Original post written on June 25, 2020

Using Sets As Functions

One of the nice things in Clojure is that some data structures are also functions. For me this felt rather strange when learning Clojure (coming from Java), but it can be very powerful. A set in Clojure is also a function. The set as function accept a single argument and it return nil when the argument is not part of the set, otherwise the argument value is returned. This behaviour also makes a set as function a nice predicate to be used for example in collection functions.

In the following example code we use different sets as function:

(ns mrhaki.set.function
  (:require [clojure.test :refer [is]]))

;; Sample set with some JVM languages.
(def languages #{"Clojure" "Groovy" "Kotlin"})

;; We can use the set languages as function
;; with one argument to check if the argument
;; is part of the set.
(is (= "Clojure" (languages "Clojure")))

;; If the argument is not part of the set
;; we get back nil.
(is (= nil (languages "Java")))


;; As nil is logical false in Clojure 
;; a set makes a nice predicate.
(is (= ["Clojure"] (filter #{"Clojure" "Java"} languages)))
(is (= ["Kotlin" "Groovy"] (remove #{"Java" "Clojure"} languages)))


;; Sample vector with numbers.
(def numbers [0 2 1 4 2 3 1 0])

;; Use set as predicate.
(is (= [2 1 2 1] (filter #{1 2} numbers)))

;; As set #{1 2} is a function we can use it as argument
;; for other functions to create a new function.
(is (= [0 4 3 0] (filter (complement #{1 2}) numbers)))

Written with Clojure 1.10.1.

Original post written on June 19, 2020

Destructure Sequences

Clojure supports advanced destructure features. In a previous post we learned about destructuring maps, but we can also destructure vectors, list and sequences in Clojure using positional destructuring. We can define symbols for positions in the sequence to assign the value at a certain position to the symbol. The first symbol in the destructure vector gets the value of the first element in the sequence, the second symbol the value of the second element and so on. To get the remaining elements from the sequence without assigning them to specific symbols we can use & followed by a symbol. Then all remaining elements are assigned as sequence the symbol. Finally we can use :as to get the original vector, list or sequence.

The folowing examples show several destructure definitions for different type of collections and sequences:

(ns mrhaki.lang.destruct-seq
  (:require [clojure.test :refer [is]]))

(def items ["mrhaki" "Hubert Klein Ikkink" "Tilburg"])

;; Elements from the items vector are positionally
;; destructured to symbols.
(let [[alias name city] items]
  (is (= "mrhaki" alias))
  (is (= "Hubert Klein Ikkink" name))
  (is (= "Tilburg" city)))

;; When we define a symbol but there are no elements
;; to assign a value, the symbol will be nil.
(let [[alias name city country] items]
  (is (nil? country)))

;; When we don't need the destructured symbol we can
;; use the underscore to indicate this. But any name will do.
(let [[username _ city] items]
  (is (= "mrhaki lives in Tilburg" 
         (str username " lives in " city))))

;; We can destructure sequences just like vectors.
(def coords '(29.20090, 12.90391))

(let [[x y] coords]
  (is (= 29.20090 x))
  (is (= 12.90391 y)))

(let [[first-letter _ third-letter] "mrhaki"]
  (is (= \m first-letter))
  (is (= \h third-letter)))


;; We can nest our destructure definitions.
(def currencies [[42 "EUR"] [50 "USD"]])

;; We want the second value of the first element and
;; the first value of the second element.
(let [[[_ currency] [amount _]] currencies]
  (is (= "EUR" currency))
  (is (= 50 amount)))

;; Example sequence with fruit names.
(def basket '("Apple" "Pear" "Banana" "Grapes" "Lemon"))

;; We can use & to assign all remaining not-yet 
;; destructured element to a sequence.
(let [[first second & rest] basket]
  (is (= "Apple" first))
  (is (= "Pear" second))
  (is (= ["Banana" "Grapes" "Lemon"] rest)))

;; We can use :as to get the original sequence.
(let [[first _ third :as fruits] basket]
  (is (= "Apple" first))
  (is (= "Banana" third))
  (is (= "APBGL" (apply str (map #(.charAt % 0) fruits)))))


;; Use destructure in function parameter to 
;; destructure the argument value when invoked.
(defn summary
  [[first second :as all]]
  (str first ", " second " and " (- (count all) 2) " more fruit names."))

(is (= "Apple, Pear and 3 more fruit names."
       (summary basket)))

Written with Clojure 1.10.1.

Original post written on February 9, 2021

Destructuring Maps

When we want to assign key values in a map to symbols we can use Clojure's powerful destructure options. With destructuring a map we can use dense syntax to assign keys to new symbols. For example we can use that in a let special form to assign symbols, but also for function parameters that are a map. When we use it for function parameters we can immediately assign keys to symbols we want to use in the function. Clojure provides a simple syntax to destructure a key value to a symbol using {symbol key} syntax. The value of :key will be assigned to symbol. We can provide default values if a key is not set in the map using :or followed by the symbol and default value. This is very useful if we know not all keys in a map will have values. Finally there is a shorthand syntax to assign keys to symbols with the same name as the key: :keys. We must provide a vector to :keys with the name of the keys, which will automatically assigned to symbols with the same name. To use this destructuring to its fullest the keys in the map must be keywords. We can use the keywordize-keys function in the clojure.walk namespace if we have a map with string keys and we want to transform them to keywords.

In the following example code we see several example of map destructuring:

(ns mrhaki.lang.destruct-map
  (:require [clojure.test :refer [is]]))

;; Sample map structure we want to destructure.
(def user {:first-name "Hubert"
           :last-name  "Klein Ikkink"
           :alias      "mrhaki"})

;; We can define a symbol username that will have the
;; the value of the :alias key of the user map.
(let [{username :alias} user]
  (is (= "mrhaki" username)))

;; When we use a non-existing key the symbol will
;; have a nil value, like the symbol city in the 
;; following example.
(let [{username :alias city :city} user]
  (is (nil? city))
  (is (= "mrhaki" username)))

;; We can use :or to define a value when a key
;; is not available in the map.
;; Here we define "Tilburg" as default value if
;; the :city key is missing from the map.
(let [{username :alias city :city :or {city "Tilburg"}} user]
  (is (= "Tilburg" city))
  (is (= "mrhaki" username)))

;; The symbol names must match in the definition
;; for the key value and the :or value.
(let [{username :alias lives-in :city :or {lives-in "Tilburg"}} user]
  (is (= "Tilburg" lives-in))
  (is (= "mrhaki" username)))

;; We can use :as to assign the original map
;; to a symbol, that we can use in the code.
(let [{username :alias :as person} user]
  (is (= "Hubert" (:first-name person)))
  (is (= "Klein Ikkink" (:last-name person)))
  (is (= "mrhaki" username)))

;; If the symbol name matches the key name we
;; can use :keys to define that so we have to type less.
(let [{:keys [alias first-name last-name]} user]
  (is (= "mrhaki" alias))
  (is (= "Hubert" first-name))
  (is (= "Klein Ikkink" last-name)))

;; Combination of destruturing options for a map.
(let [{:keys [first-name last-name city]
       :or   {city "Tilburg"}
       :as   person} user]
  (is (= "Hubert" first-name))
  (is (= "Klein Ikkink" last-name))
  (is (= "Tilburg" city))
  (is (= "mrhaki" (:alias person))))


;; Use destructuring in a function argument.
(defn who-am-i
  [{:keys [first-name last-name city]
    :or   {city "Tilburg"}
    :as   person}]
  (str first-name " " last-name ", aka " (:alias person) ", lives in " city))

(is (= "Hubert Klein Ikkink, aka mrhaki, lives in Tilburg"
       (who-am-i user)))
       
       
;; Another map with string keys. 
(def string-map {"alias" "mrhaki" "city" "Tilburg"})

(let [{username "alias" city "city"} string-map]
  (is (= "mrhaki" username))
  (is (= "Tilburg" city)))

;; We can use :strs instead of :keys for string keys.
(let [{:strs [alias city]} string-map]
  (is (= "mrhaki" alias))
  (is (= "Tilburg" city)))

;; Or convert string keys to keywords.
(let [{:keys [alias city]} (keywordize-keys string-map)]
  (is (= "mrhaki" alias))
  (is (= "Tilburg" city)))


;; For completeness we can destructure symbol keys.
(def sym-map {'alias "mrhaki" 'name "Hubert Klein Ikkink"})

(let [{username 'alias} sym-map]
  (is (= "mrhaki" username)))

;; We can use :str instead of :keys.
(let [{:syms [alias name]} sym-map]
  (is (= "mrhaki" alias))
  (is (= "Hubert Klein Ikkink" name)))

Written with Clojure 1.10.1.

Original post written on February 9, 2021

Functions

Create New Function Complementing Other Function

The Clojure function complement can be used to created a new function that returns the opposite truth value of the old function. The new function accepts the same number of arguments as the old function. Also when we invoke the new function created by the complement the old function is actually invoked and the result is used as argument for the not function to return the opposite truth value. So if the original function returns false or nil the result for the new function is true.

In the following example code we create a new function bad-weather that is the complement of good-weather:

(ns mrhaki.core.complement
  (:require [clojure.test :refer [is]]))

;; List with some temperatures for dates.
(def weather [{:date #inst "2020-06-05" :temperature 21}
              {:date #inst "2020-06-06" :temperature 17}
              {:date #inst "2020-06-07" :temperature 19}
              {:date #inst "2020-06-08" :temperature 25}
              {:date #inst "2020-06-09" :temperature 26}])

(defn good-weather
  "'Good' weather is when the temperature is between 20 and 30 degrees Celcius."
  [{temp :temperature}]
  (< 20 temp 30))

;; The opposite can easily be turned into a 
;; new function with complement. 
;; The bad-weather takes the same argument (temperature)
;; and returns false when good-weather would return true
;; and true when good-weather returns false.
(def bad-weather (complement good-weather))

(is (= [{:date #inst "2020-06-05" :temperature 21}
        {:date #inst "2020-06-08" :temperature 25}
        {:date #inst "2020-06-09" :temperature 26}]
       (filter good-weather weather)))

(is (= [{:date #inst "2020-06-06" :temperature 17}
        {:date #inst "2020-06-07" :temperature 19}]
       (filter bad-weather weather)))

;; Filtering on the good-weather and complement bad-weather
;; should return the same number of items as in the original collection.
(is (= 5
       (count weather)
       (count (concat (filter good-weather weather) (filter bad-weather weather)))))

Written with Clojure 1.10.1.

Original post written on June 15, 2020

Repeating A Value Or Function Invocation

In Clojure we can use the repeat function to get an infinite sequence of a given value. We can pass a length argument to get a fixed size sequence of the value. Clojure also provides the repeatedly function that takes as argument a function without arguments. A infinite sequence of invocations of the function is returned. Just like with the repeat function we can pass a length argument so the returned sequence has a fixed size.

We use the repeat and repeatedly function in the following example:

(ns mrhaki.core.repeat
  (:require [clojure.test :refer [is]])
  (:import (java.time LocalTime)))


;; repeat creates an infinite sequence of the given value.
(is (= ["Clojure" "Clojure" "Clojure"] 
       (take 3 (repeat "Clojure"))))

;; We can pass a length argument to restrict the number of 
;; times the value is repeated.
(is (= ["rocks" "rocks" "rocks"]
       (repeat 3 "rocks")))

(defn parrot-talk 
  [s]
  (repeat 2 s))

(is (= ["Polly wants a cracker" "Polly wants a cracker"]
       (parrot-talk "Polly wants a cracker")))


(defn before?
  "Helper function returns true if t1 is before t2, false otherwise"
  [[^LocalTime t1 ^LocalTime t2]]
  (.isBefore t1 t2))

(defn current-time 
  "Return current time"
  []
  (LocalTime/now))

;; repeatedly create an infinite sequence of function invocations.
;; The function must have no arguments.
(is (before? (take 2 (repeatedly current-time))))

(is (before? (repeatedly 2 current-time)))


(defn latest-time
  "Get the 'latest' time from a collection with time values."
  [coll]
  (reduce (fn [latest time] (if (.isAfter latest time) latest time)) coll))

(def current-times (repeatedly 100 current-time))

;; as each time is later than the next time value
;; the following equation must be true.
(is (= (latest-time current-times) (last current-times)))

Written with Clojure 1.10.1.

Original post written on June 8, 2020

Composing Functions With comp

In Clojure we can use the comp function to create a new function based on multiple other functions. We create a new function by composing other functions. We can invoke the function that is returned from the comp function with input arguments. These arguments are applied to the right most function that was used with comp. Then the output of that function is the input for the next function.

In the following example code we see several usages of the comp function. Also we see how the ordening of the functions can change the output:

(ns mrhaki.core.comp
  (:require [clojure.test :refer [is]]
            [clojure.string :as str]))

;; Some functions that work with numbers.
(defn f [x] (+ 11 (- x 90)))
(defn g [x] (* 2 x))

;; Use comp to create a new function based
;; on functions f and g.
;; Function are applied from right to left,
;; so first f and then g.
(is (= 42 ((comp g f) 100)))

;; Notice ordering is important.
;; In this case first g, then f is applied.
(is (= 121 ((comp f g) 100)))


;; User data to work with.
(def user {:name "Hubert" :alias "mrhaki"})

;; Simple function to repeat the value twice.
(def duplicate (partial repeat 2))

;; Compose new function from functions
;; :alias (keyword function), 
;; str/capitalize and
;; duplicate.
;; Function are applied from right to left.
(is (= '("Mrhaki" "Mrhaki")
       ((comp duplicate str/capitalize :alias) user)))

;; Other alternatives to chaining functions
(is (= '("Mrhaki" "Mrhaki")
       ;; using an anonymous function
       (#(duplicate (str/capitalize (:alias %1))) user)
       ;; or thread macro
       (-> user :alias str/capitalize duplicate)
       ;; or simply nested functions.
       (duplicate (str/capitalize (:alias user))))) 

Written with Clojure 1.10.1.

Original post written on May 20, 2020

Set Default Value For nil Function Argument With fnil

The function fnil can be used to create a new function from an existing function where we define default values for arguments that can be nil. Especially when we want to use a function that we didn't write ourselves this can be helpful to handle nil values in a consistent way. We can prevent for example a NullPointerException by setting a default value. We can define default values for a maximum of three arguments for the original function. When we invoke the function that is returned from the fnil function any extra arguments after the first arguments are simply passed on to the original function.

In the following example code we define a new times function based on the * function. In the example we define a default value for the first three arguments.

(ns mrhaki.core.fnil
  (:require [clojure.test :refer [is]]))

;; + throws NullPointerException when argument is nil.
(is (thrown? NullPointerException (+ nil 1)))

;; Create new function with default 0 when argument is nil.
(is (= 1 ((fnil + 0) nil 1)))


(def times 
  "Return function * with 
  default value 1 for argument 1 when nil,
  default value 10 for argumen 2 when nil and
  default value 100 for argument 3 when nil."
  (fnil * 1 10 100))

(is (= 2 
       (times nil 2)
       (* 1 2)))

(is (= 20 
       (times 2 nil) 
       (* 2 10)))

(is (= 2000 
       (times 2 nil nil) 
       (* 2 10 100)))

(is (= 200
       (times nil 2 nil)
       (* 1 2 100)))

(is (= 20
       (times nil nil 2)
       (* 1 10 2)))

;; We can use more arguments that are passed
;; to the original * function.
(is (= 4000 
       (times 2 nil nil 2)
       (* 2 10 100 2)))

(is (= 1000
       (times nil nil nil)
       (* 1 10 100)))

(is (= 2000
       (times nil nil nil 2)
       (* 1 10 100 2)))

Written with Clojure 1.10.1.

Original post written on May 12, 2020

Getting Results From Multiple Functions With Juxt Function

The function juxt can be used to get results from multiple functions on the same argument in one go. We pass the functions we want to invoke as arguments to the juxt function. This results in a new function that will return a vector with the results from each original function that is passed to the juxt function. So the first element in the result vector is the result of the first function, the second element is the result of the second function and so on. The documentation of the juxt function shows this clearly as ((juxt a b c)) => [a(x) b(x) c(x)].

In the following example we use the juxt function to apply multiple functions on a string, collection and map:

(ns mrhaki.core.juxt
  (:require [clojure.test :refer [is]]))

;; The functions first, last and count are applied
;; to the string value and we get a vector
;; with the result of each function.
(is (= [\C \e 18]
       ((juxt first last count) "Clojure is awesome")))


(defn sum 
  "Calculate sum of values in collection"
  [coll] 
  (apply + coll))

(defn avg 
  "Calculate average of values in collection"
  [coll] 
  (/ (sum coll) (count coll)))

;; Create new function summarize to give back statistics
;; on a collection based on count, min, max, sum and average.
(def summarize (juxt count #(apply min %) #(apply max %) sum avg))

(is (= [4 1 108 171 171/4]
       (summarize [1 20 42 108])))

(let [[count min max sum avg] (summarize [1 20 42 108])]
  (is (= 4 count))
  (is (= 1 min))
  (is (= 108 max))
  (is (= 171 sum))
  (is (= 171/4 avg)))


;; As keywords are functions we can use them to get values
;; for keys in a map.
(is (= ["Hubert" "mrhaki"]
       ((juxt :name :alias) {:alias "mrhaki" :name "Hubert" :location "Tilburg"})))

Written with Clojure 1.10.1.

Original post written on April 17, 2020

Pure Function Sample Buying Coffee From FP Programming In Scala Written In Clojure

Some of our colleagues at JDriven work with Scala and we talked about the book Functional Programming in Scala written by Paul Chiusano and Runar Bjarnason. We looked at one of the examples in the first chapter in the book to show the importance of having pure functions without side effects. The example is about buying a cup of coffee and charging a credit card with the purchase. In three examples a function with side effects is refactored to a pure function without side effects. When looking at the example I was wondering how this would look like in Clojure using only functions and simple immutable data structures. We can look at the examples in Scala to see how it is explained and implemented in the book. There are also Kotlin samples available. In this post we see a possible implementation in Clojure to buy coffee and charge our credit card.

The first example is a buy-coffee function with a credit card type as parameter. When we invoke the function with a credit card argument the credit card gets charged as side effect and a coffee type is created and returned. The coffee type is simply a map with a price key.

(ns mrhaki.coffee.cafe1)

(defn charge
  "Charge credit card with given amount.
   This function has side effects with a println in our example,
   but in a real world implementation could also include
   REST API calls, database access or other side effects."
  [cc amount]
  (println (str "Charge " amount " to credit card " (:number cc))))

(defn buy-coffee
  "Sample function to buy a coffee and charge the given credit card
   with the price of the coffee.
   The function returns coffee."
  [cc]
  (let [cup {:price 2.99}]
    (charge cc (:price cup))
    cup))

Let’s write a simple test for the buy-coffee function:

(ns mrhaki.coffee.cafe1-test
  (:require [clojure.test :refer :all]
            [mrhaki.coffee.cafe1 :refer :all]))

(deftest buy-coffee-test
  (is (= {:price 2.99} (buy-coffee {:number "123-456"})))
  (is (= {:price 2.99} (buy-coffee {:number "456-789"}))))

The test runs and shows the function works as expected, but we cannot test the side effect. If the side effect would involve calls to other systems, those systems would be called when we run our tests. That is something we would try to avoid, because it makes testing in isolation more difficult.

In the Scala example the next step is to pass in a Payment type that will take care of charging the credit card. This would make testing the function easier, because we can pass a testable implementation of the Payment type (e.g. a mock). So at least in our tests we can have an implementation that doesn’t access external systems.

For our Clojure example we can pass a payment function as argument to the buy-coffee function. This function takes a credit card type and amount as arguments and will be invoked in the buy-coffee function.

(ns mrhaki.coffee.cafe2)

(defn buy-coffee
  "Sample function to buy coffee and charge the given credit card
   with the price of the coffee.
   The function to charge the credit card is given as second argument.
   The fn-payment function must support a credit card as first
   argument and an amount as second argument."
  [cc fn-payment]
  (let [cup {:price 2.99}]
    (fn-payment cc (:price cup))
    cup))

When we write a test we can now check how the payment function is invoked:

(ns mrhaki.coffee.cafe2-test
  (:require [clojure.test :refer :all]
            [mrhaki.coffee.cafe2 :refer :all]))

(deftest buy-coffee-test
  (let [payment (fn [cc amount]
                  (is (= "123-456" (:number cc)))
                  (is (= 2.99 amount)))]
    (is (= {:price 2.99}
           (buy-coffee {:number "123-456"} payment)))))

But our buy-coffee function is not pure. We have still the side effect, but we made the function better testable. It is also mentioned in Functional Programming in Scala that if we want to invoke our buy-coffee function multiple times, the credit card also gets charged multiple times and this would include extra processing fees as well.

The final implementation of the buy-coffee will no longer charge the credit card, but it will a charge type with all information needed for charging the credit card, together with returning the coffee type. In another function we can handle the actual charging of the credit card by invoking REST calls, database calls or what else is needed. But now it is no longer a concern of our buy-coffee function. Also we can now invoke the buy-coffee function multiple times and combine all returned charge types into a single charge, to save on processing fees.

In our Clojure example we change the buy-coffee function and return both a coffee type and a charge type. The charge type is a map with a key for the credit card and a key for the amount to be charged. Also we introduce the buy-coffees function to show how we can invoke buy-coffee multiple times and combine the charges into a single charge.

(ns mrhaki.coffee.cafe3)

(defn buy-coffee
  "Sample function to buy coffee.
   The function returns a vector with coffee as first element and
   a charge type as second element."
  [cc]
  (let [cup {:price 2.99}]
    [cup {:credit-card cc :amount (:price cup)}]))

(defn- combine
  "Return a new charge with the sum of the amount values when
   the credit card number keys are equal.
   Throws exception when credit card numbers are not equal."
  [charge1 charge2]
  (if (= (:number charge1) (:number charge2))
    (update-in charge1 [:amount] + (:amount charge2))
    (throw (Exception. "Can't combine charges to different cards."))))

(defn- unzip
  "Returns pair of collections where first collection is built from first
   values of each pair in the coll argument and the second collection
   is built from the second value of each pair."
  [coll]
  (reduce (fn [[coll-a coll-b] [a b]]
            [(conj coll-a a) (conj coll-b b)])
          [[] []]
          coll))

(defn buy-coffees
  "Buy multiple times a cofee and combine all charges for given credit card.
  The first parameter accepts a credit card type and
  the second parameter is the number of times a coffee is bought."
  [cc n]
  (let [[coffees charges] (unzip (repeatedly n #(buy-coffee cc)))]
    [coffees (reduce combine charges)]))

And testing the functions is now much easier:

(ns mrhaki.coffee.cafe3-test
  (:require [clojure.test :refer :all]
            [mrhaki.coffee.cafe3 :refer :all]))

(deftest buy-coffee-test
  (let [[coffee charge] (buy-coffee {:number "123-456"})]
    (is (= {:price 2.99} coffee))
    (is (= {:credit-card {:number "123-456"} :amount 2.99} charge))))

(deftest buy-coffees-test
  (let [[coffees charge] (buy-coffees {:number "123-456"} 2)]
    (is (= (repeat 2 {:price 2.99}) coffees))
    (is (= {:credit-card {:number "123-456"} :amount 5.98} charge))))

The charge type also makes it easier to work with the charges. For example we can add a coalesce function that takes a collection of charge types and returns all charges per credit card. We can use this information to minimize the processing fees with the credit card company.

(ns mrhaki.coffee.cafe3)

...

(defn coalesce
  "Coalesce same card charges."
  [charges]
  (->> charges
       (group-by :credit-card)
       (vals)
       (map #(reduce combine %))))

And we can write the following test:

(ns mrhaki.coffee.cafe3-test
  (:require [clojure.test :refer :all]
            [mrhaki.coffee.cafe3 :refer :all]))
...

(deftest coalesce-test
  (let [charge-1a {:credit-card {:number "123-456"} :amount 2.99}
        charge-2a {:credit-card {:number "456-789"} :amount 2.99}
        charge-1b {:credit-card {:number "123-456"} :amount 2.99}
        charges (coalesce [charge-1a charge-2a charge-1b])]
    (is (= {:credit-card {:number "123-456"} :amount 5.98} (first charges)))
    (is (= {:credit-card {:number "456-789"} :amount 2.99} (second charges)))))

In the examples we use a map as type for the coffee, credit card and charge types. We can add records for these types to our Clojure examples to have some more semantics in our code. The good thing is that a records still can be used queries as a map, so a keyword function like :price still works for a Coffee record.

In the next example we add records and use them in the buy-coffee function. Notice the other functions still work without changes.

(ns mrhaki.coffee.cafe4)

;; Define records.
(defrecord Coffee [price])
(defrecord CreditCard [number])
(defrecord Charge [credit-card amount])

(defn buy-coffee
  "Sample function to buy coffee.
   The function returns a vector with coffee as first element and
   a charge type as second element."
  [cc]
  (let [cup (->Coffee 2.99)
        charge (->Charge cc (:price cup))]
    [cup charge]))

(defn- combine
  "Return a new charge with the sum of the amount values when
   the credit card number keys are equal.
   Throws exception when credit card numbers are not equal."
  [charge1 charge2]
  (if (= (:number charge1) (:number charge2))
    (update-in charge1 [:amount] + (:amount charge2))
    (throw (Exception. "Can't combine charges to different cards."))))

(defn- unzip
  "Returns pair of collections where first collection is built from first
   values of each pair in the coll argument and the second collection
   is built from the second value of each pair."
  [coll]
  (reduce (fn [[coll-a coll-b] [a b]]
            [(conj coll-a a) (conj coll-b b)])
          [[] []]
          coll))

(defn buy-coffees
  "Buy multiple times a cofee and combine all charges for given credit card.
  The first parameter accepts a credit card type and
  the second parameter is the number of times a coffee is bought."
  [cc n]
  (let [[coffees charges] (unzip (repeatedly n #(buy-coffee cc)))]
    [coffees (reduce combine charges)]))

(defn coalesce
  "Coalesce same card charges."
  [charges]
  (->> charges
       (group-by :credit-card)
       (vals)
       (map #(reduce combine %))))

In our tests we can now also use the functions to create a Coffee, CreditCard and Charge records:

(ns mrhaki.coffee.cafe4-test
  (:require [clojure.test :refer :all]
            [mrhaki.coffee.cafe4 :refer :all]))

(deftest buy-coffee-test
  (let [[coffee charge] (buy-coffee (->CreditCard "123-456"))]
    (is (= (->Coffee 2.99) coffee))
    (is (= (->Charge (->CreditCard "123-456") 2.99)) charge)))

(deftest buy-coffees-test
  (let [[coffees charge] (buy-coffees (->CreditCard "123-456") 2)]
    (is (= (repeat 2 (->Coffee 2.99)) coffees))
    (is (= (->Charge (->CreditCard "123-456") 5.98)) charge)))

(deftest coalesce-test
  (let [cc1     (->CreditCard "123-456")
        cc2     (->CreditCard "456-789")
        charges (coalesce [(->Charge cc1 2.99)
                           (->Charge cc2 2.99)
                           (->Charge cc1 2.99)])]
    (is (= (->Charge (->CreditCard "123-456") 5.98)) (first charges))
    (is (= (->Charge (->CreditCard "456-789") 2.99)) (second charges))))

Written with Clojure 1.10.1

Original post written on March 8, 2021

Strings

Formatting With Java Format String

In Clojure we can format a string using Common Lisp format syntax or the Java format string syntax. In the post we will look at the how we can use the Java format string syntax. We must use the format function in the clojure.core namespace. The method delegates to the standard JDK String#format method. The first argument is a format string followed by one or more arguments that are used in the format string. We can look up the syntax of the format string in the Javadoc for the java.util.Formatter class.

In the following example code we use the format function with different format strings:

(ns mrhaki.string.format
  (:require [clojure.test :refer [is]])
  (:import (java.util Locale)))

;; Create new string with format string as template as first argument.
;; Following arguments are used to replace placeholders in the
;; format string.
;; Clojure will delegate to the java.lang.String#format method and
;; we can use all format string options that are defined for this method.
;; More details about the format string syntax can be found in
;; java.util.Formatter. In a REPL we can find the docs 
;; with (javadoc java.util.Formatter).
(is (= "https://www.mrhaki.com/"
       (format "https://%s/" "www.mrhaki.com")))

;; Format string with argument index to refer to one argument twice.
(is (= "clojure CLOJURE"
       (format "%1$s %1$S" "clojure")))

;; Format string to define fixed result lenght of 10 characers
;; with padding to get the given length.
(is (= "   Clojure"
       (format "%10s" "Clojure")))

;; Default Locale is used to determine how locale specific 
;; formats are applied. In the following example the default
;; decimal separator is . and group separator is , as specified
;; for the Canadian Locale.
(Locale/setDefault Locale/CANADA)
(is (= "Total: 42,000.00"
       (format "Total: %,.2f", 42000.0)))

(defn format-locale
  "Format a string using String/format with a Locale parameter"
  [locale fmt & args]
  (String/format locale fmt (to-array args)))

;; We can use a different Locale to apply different specific 
;; locale formats. In the next example we use the Dutch Locale
;; and the decimal seperator is , and the group separator is ..
(is (= "Totaal: 42.000,00"
       (format-locale (Locale. "nl") "Totaal: %,.2f" 42000.0)))

Written with Clojure 1.10.1.

Original post written on October 10, 2020

Trimming Strings

In the clojure.string namespace we can find several useful function for working with strings. If we want to trim a string we can choose for the trim, trial, trimr and trim-newline functions. To trim all characters before a string we must use the triml function. To remove all space characters after a string we use trimr. To remove space characters both before and after a string we can use the trim function. Finally if we only want to remove the newline and/or return characters we use the trim-newline function.

In the following example we use the different trim functions on strings:

(ns mrhaki.sample
  (:require [clojure.test :refer [is]]
            [clojure.string :refer [trim triml trimr trim-newline]]))

;; The trim function removes spaces before and after the string.
(is (= "mrhaki" (trim " mrhaki ")))
;; Tabs are also trimmed.
(is (= "mrhaki" (trim "\t mrhaki ")))
;; Return and/or newline characters are also trimmed.
(is (= "mrhaki" (trim "\tmrhaki \r\n")))
;; Character literals that should be trimmed are trimmed.
(is (= "mrhaki" (trim (str \tab \space " mrhaki " \newline))))

;; The triml function removes spaces before the string (trim left).
(is (= "mrhaki " (triml " mrhaki ")))
(is (= "mrhaki " (triml "\t mrhaki ")))
(is (= "mrhaki " (triml "\nmrhaki ")))
(is (= "mrhaki " (triml (str \return \newline " mrhaki "))))

;; The trimr function removes spaces after the string (trim right).
(is (= " mrhaki" (trimr " mrhaki ")))
(is (= " mrhaki" (trimr " mrhaki\t")))
(is (= " mrhaki" (trimr (str " mrhaki " \newline))))

;; The trim-newline function removes only newline from string.
(is (= "mrhaki " (trim-newline (str "mrhaki " \newline))))
(is (= "mrhaki " (trim-newline (str "mrhaki " \return \newline))))
(is (= "mrhaki " (trim-newline "mrhaki \r\n")))
(is (= "mrhaki " (trim-newline "mrhaki ")))

Written with Clojure 1.10.1.

Original post written on January 3, 2020

Check Substring Is Part Of String

Sometimes we want to see if a string is part of another string. Or if the string value starts or ends with a certain string. In Clojure we can use the includes? function from the clojure.string namespace to check if a string is part of another string value. To see if a string starts with a certain value we can use the starts-with? function from the clojure.string namespace. And to check if a string ends with a given value we use ends-with? from the same namespace.

In the next example code we use these functions and also add a matches? function to check if a string matches with a regular expression defined in a string:

(ns mrhaki.string.includes
  (:require [clojure.string :as str]
            [clojure.test :refer [is]]))

;; String to check.
(def s "Clojure is cool!")

;; Check if given value is part of the string.
(is (true? (str/includes? s "cool")))
(is (false? (str/includes? s "boring")))

;; Check string starts with given value.
(is (true? (str/starts-with? s "Clojure")))
(is (false? (str/starts-with? s "Groovy")))

;; Check string ends with given value.
(is (true? (str/ends-with? s "cool!")))

;; Helper function to see if string with regular expression
;; matches a given string value using java.lang.String#matches.
(defn matches? 
  "Return true when string `re` with regular expression
  matches for value `s`, false otherwise."
  [^CharSequence s ^CharSequence re] 
  (. s matches re))

(is (true? (matches? s ".*is.*")))
(is (false? (matches? s "cool")))

Written with Clojure 1.10.1.

Original post written on April 22, 2020

Splitting Strings

In Clojure we can use the clojure.string/split function to split a string, based on a regular expression, into a vector with string values. Optionally we can also specify a limit on the maximum number of returned string values we want. If we want to split a string based on the newline characters we can use the function clojure.string/split-lines that returns a vector where each element is a line from the original multi-line string.

The following example shows several usages of the split and split-lines functions:

(ns mrhaki.string.split
  (:require [clojure.string :as str]
            [clojure.test :refer [is are]]))

;; Sample string to split.
(def issue "CLJ-90210: Subject")

;; Split on - and : to get vector with string values.
(is (= ["CLJ" "90210" "Subject"] (str/split issue #"-|: ")))

;; The split function accepts a third argument that is
;; a limit on the number of splits that are returned.
(is (= ["CLJ" "90210" "Subject"] 
       (str/split issue #"-|: " 0)
       (str/split issue #"-|: " 3)))

(is (= [issue] (str/split issue #"-|: " 1)))
(is (= ["CLJ" "90210: Subject"] (str/split issue #"-|: " 2)))


;; Multiline sample string to split per line and
;; the split each line.
(def itinerary "loc:LVG time:15h-16h activity:Binge-watching
loc:DNR time:18h-19h activity:Eating
loc:MBR time:23h-7h activity:Sleeping")

;; Using split-line function we get a vector
;; where each line is an element.
;; Then for each line we split on : and \s+ and 
;; convert it to a map.
;; E.g. first line is 
;; {"loc" "LVG" "time" "15h-16h" "activity" "Binge-watching"}
(def agenda (map #(apply hash-map (str/split % #":|\s+"))
                 (str/split-lines itinerary)))

(is (= "LVG" ((first agenda) "loc")))
(is (= "15h-16h" ((first agenda) "time")))
(is (= "Binge-watching" ((first agenda) "activity")))

(is (= "DNR" ((nth agenda 1) "loc")))
(is (= "18h-19h" ((nth agenda 1) "time")))
(is (= "Eating" ((nth agenda 1) "activity")))

(are [value m key] (= value ((last m) key))
                   "MBR" agenda "loc"
                   "23h-7h" agenda "time"
                   "Sleeping" agenda "activity")

Written with Clojure 1.10.1.

Original post written on April 1, 2020

Joining Elements in a Collection

We can use the join function from the clojure.string namespace to join elements from a collection into a string. We can optionally specify a separator that is used to separate each element in the string output. The separator is not used after the last element of the collection. If we don't specify a separator the elements are concatenated without separation. The string representation for each element in the collection is used in the joined end result.

In the following example code we see different usages of the join function:

(ns mrhaki.sample
  (:import [java.util Currency Locale])
  (:require [clojure.string :refer [join]]
            [clojure.test :refer [is]]))

;; Join without explicit separator simply concats values in collection.
(is (= "abc" (join ["a" "b" "c"])))

;; Join with separator uses separator between elements from collection
;; and omits the separator after the last element.
(is (= "a, b, c" (join ", " ["a" "b" "c"])))

;; Join works on multiple collection types, 
;; because each collection is transformed to a seq.
(is (= "a::b::c" (join "::" #{"a" "b" "c"})))

;; Collection with non-strings is also returned as string.
;; The string representation of each element is used.
(is (= "0 1 2 3 4 5 6 7 8 9 10" (join " " (range 11))))
(is (= "https://www.mrhaki.com:443/,EUR" (join \, [(java.net.URL. "https" "www.mrhaki.com" 443 "/")
                                                    (Currency/getInstance (Locale. "nl" "NL"))])))

;; Nil values are ignored in the join results, 
;; but separator is still used for nil element.
(is (= "Clojure--is cool--!" (join "-" ["Clojure" nil "is cool" nil "!"])))

;; Function query-params to transform a map structure with 
;; keyword keys to URL request parameters.
(defn query-params 
  "Return key/value pairs as HTTP request parameters separated by &.
   Each request parameter name and value is separated by =.
   E.g. {:q \"Clojure\" :max 10 :start 0 :format \"xml\"} is transformed
   to q=Clojure&max=10&start=0&format=xml."
  [params]
  (let [query-param (fn [[param-name param-value]] (join "=" [(name param-name) param-value]))]
    (join "&" (map query-param params))))

(is (= "q=Clojure&max=10&start=0&format=xml" (query-params {:q "Clojure" :max 10 :start 0 :format "xml\
"})))

Written with Clojure 1.10.1

Original post written on January 6, 2020

Replacing Characters In A String With escape Function

The clojure.string namespace contains a lot of useful functions to work with string values. The escape function can be used to replace characters in a string with another character. The function accepts as first argument the string value and the second argument is a map. The map has characters as key that need to be replaced followed by the value it is replaced with. For example the map {\a 1 \b 2} replaces the character a with 1 and the character b with 2.

In the following example code we use the escape function in several cases:

(ns mrhaki.string.escape-string
  (:require [clojure.string :as str]
            [clojure.test :refer [is]]))

(is (= "I 10v3 C10jur3"
       (str/escape "I love Clojure" {\o 0 \e 3 \l 1})))

(is (= "mrHAKI" 
       (str/escape "mrhaki" {\h "H" \a "A" \k "K" \i "I" \x "X"})))

(def html-escaping {(char 60) "&lt;" (char 62) "&gt;" (char 38) "&amp;"})
(is (= "&lt;h1&gt;Clojure &amp; Groovy rocks!&lt;/h1&gt;"
       (str/escape "<h1>Clojure & Groovy rocks!</h1>" html-escaping)))

(is (= "Special chars: \\t \\n"
       (str/escape "Special chars: \t \n" char-escape-string)))

Written with Clojure 1.10.1.

Original post written on July 8, 2020

Replacing Matching Values In String

We can search for a value in a string and replace it with another value using the clojure.string/replace function. The first parameter is the original string value that we want to replace parts of. The second parameter can be a string value or regular expression. The last parameter is the replacement value that can be a string value or a function that returns a string value. The function itself gets either a string argument if the match has no nested groups (when match is a regular expression) or a vector with a complete match followed by the nested groups when the match has nested groups.

In the following example we several invocation of the clojure.string/replace function with different arguments:

(ns mrhaki.string.replace
  (:require [clojure.string :as str]
            [clojure.test :refer [is]]))

;; Example string value to do replacements on.
(def s "Programming with Clojure is fun!")

;; Match argument can be a string value,
;; that gives same result as java.lang.String#replace method.
(is (= "Programming with Clojure is awesome!"
       (str/replace s "fun" "awesome")
       (.replace s "fun" "awesome")))


;; Match argument can also be regular expression pattern.
(is (= "Programming_with_Clojure_is_fun!"
       (str/replace s #"\s+" "_")))

;; When the regular expression pattern has groups
;; we can refer to them using $ followed by matched
;; group number, eg. $1 for the first group.
(is (= "Execution 1 took 200ms"
       (str/replace "run1=200ms" #"run(\d+)=(\d+ms)" "Execution $1 took $2")))


;; Replace argument can be a function.
;; Argument of the function is string of entire match
;; if there are no nested groups.
(is (= "[NOTE] [CAUTION]" 
       (str/replace "[note] [caution]" #"\[\w+\]" #(.toUpperCase %))))

;; Otherwise if there are nested groups a vector is
;; used as argument for the replacment function
;; where the first argument is the
;; entire match followed by the nested groups.
(is (= "ABC def"
       (str/replace "abc DEF" 
                    #"(\w+)(\s+)(\w+)" 
                    #(str (.toUpperCase (% 1)) (% 2) (.toLowerCase (% 3))))))

;; By destructuring the vector argument
;; we can refer to the groups using a name.
(defn replacement
  [[_ execution time]]
  (let [seconds (/ (bigdec time) 1000)]
    (str "Execution " execution " took " seconds " seconds")))

(is (= "Execution 1 took 0.2 seconds"
       (str/replace "run1=200ms" #"run(\d+)=(\d+)ms" replacement)))

Written with Clojure 1.10.1.

Original post written on March 29, 2020

Collections

Merge Maps With Function To Set Value Duplicate Keys

In Clojure we can use the merge function to merge multiple maps into a single map. If a key is in multiple maps the value of the key merged last will be used in the resulting map. If we want to influence how the value of a duplicate key is set we can use merge-with. We specify as first argument the function that will be used when the same key is available in multiple maps. The function must accept two arguments, where the the first argument is the value of the key in the first map and the second argument the value of the same key in the following map. The result is assigned to the key in the resulting map. If we pass more than two maps to the merge-with the function will be called multiple times for a key if it is part of more than two maps.

In the following example we use Clojure core functions and a custom function to merge multiples maps, so we can alter the value for duplicate keys:

(ns mrhaki.core.merge-with
  (:require [clojure.test :refer [is]]))

;; Merge maps and use the function specified as first argument
;; to calculate the value for keys that are present
;; in multiple maps.
(is (= {:a 60 :b 3 :c 44 :d 100}
       (merge-with * {:a 2 :b 3 :c 4} {:a 10 :c 11} {:a 3 :d 100})))

;; Works for all maps and independent of type that is used for keys.
;; We can use any function for merge-with.
(def languages (merge-with (comp vec flatten conj) {"Clojure" [:dynamic :functional]}
                           {"Java" [:jvm]}
                           {"Groovy" [:jvm]}
                           {"Clojure" [:jvm]}
                           {"Groovy" [:dynamic]}))

(is (= {"Clojure" [:dynamic :functional :jvm]
        "Java"    [:jvm]
        "Groovy"  [:jvm :dynamic]}
       languages))


;; Sample map with small inventory.
(def inventory {"pencil" {:count 10 :price 0.25}
                "pen"    {:count 23 :price 0.4}})
;; Sample basket with items.
(def basket {"pencil" {:count 5} "pen" {:count 2}})

;; Function to subtract the :count value for a basket item
;; from the :count value for the same inventory item.
(defn item-sold
  [inventory-item basket-item]
  (update-in inventory-item [:count] - (:count basket-item)))

(is (= {"pencil" {:count 5 :price 0.25}
        "pen"    {:count 21 :price 0.4}}
       (merge-with item-sold inventory basket)))

Written with Clojure 1.10.1.

Original post written on February 23, 2021

Split Collection With Predicate

To split a collection in Clojure we can use the split-with and split-at functions. The split-with function takes a predicate as first argument and a colletion as second argument. The function will return a vector with two items. The first item is the result of the function take-while with the given predicate. The second item in the result vector is the resul of the drop-while function with the same predicate.

We use the split-at function with a number as first argument followed by a collection to split based on a given number of items. Instead of using a predicate we can define the number of items that we want as the first item in the result vector. The first item in the result vector is the result of invoking the take function. The resulting number of items of the collection will be the second item in the result vector and is achieved by invoking the drop function.

In the following example we use both functions with different arguments:

(ns mrhaki.core.split
  (:require [clojure.test :refer [is]]))

;; The split-with function has a predicate and returns the result 
;; of the functions take-while and drop-while in a result vector.
(let [less-than-5? (partial > 5)
      numbers      (range 11)]
  (is (= ['(0 1 2 3 4) '(5 6 7 8 9 10)]
         (split-with less-than-5? numbers))
         [(take-while less-than-5? numbers) (drop-while less-than-5? numbers)]))

;; In this example we take while the value is a String value and 
;; drop while starting from first value that is not a String.
(letfn [(string-value? [[k v]] (instance? String v))]
  (is (= ['([:language "Clojure"] [:alias "mrhaki"]) '([:age 47] [:country "NL"])]
         (split-with string-value? {:language "Clojure" :alias "mrhaki" :age 47 :country "NL"}))))


;; Instead of splitting on a predicate we can just give the number
;; of elements we want to split on with the split-at function.
(is (= ['(0 1 2 3) '(4 5 6 7)]
       (split-at 4 (range 8))
       [(take 4 (range 8)) (drop 4 (range 8))]))

(is (= ['([:language "Clojure"] [:alias "mrhaki"] [:age 47]) '([:country "NL"])]
       (split-at 3 {:language "Clojure" :alias "mrhaki" :age 47 :country "NL"})))

Clojure 1.10.1.

Original post written on October 14, 2020

Concatenation Of Map Function Results With mapcat

When we use a function as argument for the map function that returns a collection we would get nested collections. If we want to turn the result into a single collection we can concatenate the elements from the collections by applying the concat function, but we can do this directly with the function mapcat. The function mapcat takes as first argument a function (that returns a collection) and one or more collections as next arguments.

In the following examples we see several uses of mapcat:

(ns mrhaki.core.mapcat
  (:require [clojure.test :refer [is]]))

;; The function argument for mapcat returns a collection
;; with the original element of the collection
;; and the value added by 10.
(is (= [1 11 2 12 3 13]
       (mapcat (fn [n] [n (+ 10 n)]) [1 2 3])))

(is (= [1 1 2 2 3 3]
       (mapcat (partial repeat 2) [1 2 3])
       ;; Using apply concat with map returns the same result.
       (apply concat (map (partial repeat 2) [1 2 3]))))

;; Combined with juxt
(is (= ["mrhaki" 6 "blog" 4]
       (mapcat (juxt identity count) ["mrhaki" "blog"])))

;; Our first example rewritten with juxt.
(is (= [1 11 2 12 3 13]
       (mapcat (juxt identity (partial + 10)) [1 2 3])))

;; We can use multiple collections,
;; the function then accepts multiple arguments.
(is (= [1 100 100 2 200 400 3 300 900]
       (mapcat (fn [a b] [a b (* a b)]) [1 2 3] [100 200 300])))

Written with Clojure 1.10.1.

Original post written on July 5, 2020

Transforming Collection Items With Index

If we want to transform items in a collection we can use the map function. If we also want to use the index of the element in the collection in the transformation we must use the map-indexed function. We must provide a function with 2 arguments, where the first argument is the index of the element in the collection and the second argument is the element in the collection.

In the following examples we use the map-indexed function:

(ns mrhaki.core.map-indexed
  (:require [clojure.test :refer [is]]))

;; map-indexed applies a function to each element
;; in a collection where the function gets the
;; index of the item in the collection and the item itself.
(is (= [[0 3] [1 20] [2 10] [3 2] [4 1]]
       (map-indexed (fn [index number] [index number]) [3 20 10 2 1])))


(defn indices
  "Return lazy sequence of indices of elements in a collection."
  [coll]
  (map-indexed (fn [index _] index) coll))

(is (= [0 1 2 3 4] (indices [3 20 10 2 1])))


(defn char-range
  "Function to return a range of characters from `start` to `end` (including)."
  [start end]
  (map char (range (int start) (inc (int end)))))

(def a-z (char-range \a \z)) ;; characters from a to z.

;; map-indexed returns a lazy sequence.
(is (= [[\a 0] [\b 1] [\c 2]]
       (take 3 (map-indexed (fn [index ch] [ch index]) a-z))))


;; Create map with letter keys and position in alphabet as values.
(def letters-positions (into {} (map-indexed (fn [index ch] [ch (inc index)]) a-z)))

(is (= [[\a 1] [\b 2] [\c 3]]
       (take 3 letters-positions)))

;; Find position of each letter of word "clojure".
(is (= [3 12 15 10 21 18 5]
       (reduce (fn [result value] (conj result (get letters-positions value)))
               []
               "clojure")))

Written with Clojure 1.10.1.

Original post written on June 8, 2020

Repeat Items In A Collection As Lazy Sequence With cycle

The Clojure function cycle take a collections as argument and creates a lazy sequence by repeating the items in the collection. So if we pass a collection with the characters \a, \b and \c we get a lazy sequence of (\a \b \c \a \b \c ...).

(ns mrhaki.core.cycle
  (:require [clojure.test :refer [is]]))

;; The items in the collection are repeated
;; and return type is a lazy sequence.
(is (= [0 1 0 1 0 1] 
       (take 6 (cycle [0 1]))))
(is (seq? (cycle [0 1])))

(is (= [\C \l \o \j \u \r \e \C \l \o \j \u \r \e]
       (take 14 (cycle "Clojure"))))

;; Useful for functions that want equally sized 
;; collection arguments.
(is (= {:a 0 :b 1 :c 0 :d 1} 
       (zipmap [:a :b :c :d] (cycle [0 1]))))

Written with Clojure 1.10.1.

Original post written on June 2, 2020

Interleave Keys And Values Into A Map With zipmap

The Clojure function zipmap create a map by interleaving a collection of keys with a collection of values. The first element of the keys collection is the map entry keyword and the first element of the values collection is than the map entry value, and so on for the other elements in the keys and values collections.

In the following example code we use zipmap using different examples of keys and values collections:

(ns mrhaki.core.zipmap
  (:require [clojure.test :refer [is]]))

;; zipmap creates a map with keys from the
;; first collection and values from the second.
(is (= {:name "Hubert" :alias "mrhaki"} 
       (zipmap [:name :alias] ["Hubert" "mrhaki"])))

;; If the size of the values collection is smaller
;; than the size of the keys collection, only 
;; keys that map to a value end in up
;; in the resulting map. 
(is (= {:name "Hubert"}
       (zipmap [:name :alias] ["Hubert"])))

;; If the size of the keys collection is smaller
;; than the size of the value collection, then the
;; returned map only contains keys from the keys 
;; collection and some values are ignored.
(is (= {:name "Hubert"}
       (zipmap [:name] ["Hubert" "mrhaki"])))

;; Using a lazy sequence created by the repeat
;; function we can set a default value for all keys.
(is (= {:name "" :alias "" :city ""}
       (zipmap [:name :alias :city] (repeat ""))))

;; If we have keys with the same name the last
;; mapping ends up in the resulting map.
(is (= {:name "mrhaki"}
       (zipmap [:name :name] ["Hubert" "mrhaki"])))

;; Keys for the resulting map don't have to be keywords,
;; but can be any type.
(is (= {"name" "Hubert" "alias" "mrhaki"}
       (zipmap ["name" "alias"] ["Hubert" "mrhaki"])))

Written with Clojure 1.10.1.

Original post written on May 29, 2020

Using The range Function

In Clojure we can use the range function to create a lazy sequence of numbers. We can optionally specify a start value, end value and define the steps between the numbers. If we use the end value argument that value is exclusive for the returned values in the lazy sequence.

In the following example we invoke the range function with different arguments:

(ns mrhaki.core.range
  (:require [clojure.test :refer [is]]))

;; range function without arguments returns
;; an infinite lazy sequence of numbers.
(is (= '(0 1 2 3 4) (take 5 (range))))

;; We can specifyt the start value for 
;; a lazy infinite  sequence of numbers.
(is (= '(0 1 2 3 4) (range 5)))

;; With the second argument we set the
;; end value for our lazy sequence of numbers.
;; The end value is exclusive for the range.
(is (= '(5 6 7 8 9) (range 5 10)))

;; The third argument defines the step value
;; between numbers, which by default is 1.
(is (= '(0 2 4 6 8) (range 0 10 2)))

;; We can also have a lazy sequence counting 
;; numbers back.
(is (= '(5 4 3 2 1) (range 5 0 -1)))

(is (= '(100 97 94 91 88) (take 5 (range 100 0 -3))))

Written with Clojure 1.10.1.

Original post written on May 13, 2020

Combine First And Next Functions Multiple Times

The first function in Clojure returns the first item of a collection. The next function returns a new sequence with all elements after the first element from a collection. Clojure adds some utility methods to combine first and next with different combinations. We can use the function ffirst which is will return the first element of the first element of a collection and the nfirst function to get the next elements from the first element of a collection. We can use the function fnext to get the first element of the next elements of a collection and the function nnext to get the next elements of the next elements of a collection.

In the following example we use the ffirst, nfirst, fnext and nnext:

(ns mrhaki.seq
  (:require [clojure.test :refer [is]]))

(def langs [{:language "Clojure" :site "https://clojure.org" :dynamic true}
            {:language "Groovy" :site "https://www.groovy-lang.org" :dynamic true}
            {:language "Java" :site "https://www.java.com" :dynamic false}
            {:language "Kotlin" :site "https://kotlinlang.org" :dynamic false}])

;; Find first map entry of first map in languages.
(is (= [:language "Clojure"]
       (ffirst langs)
       (first (first langs))))

;; Find next map entries for first map in languages.
(is (= (list [:site "https://clojure.org"] [:dynamic true])
       (nfirst langs)
       (next (first langs))))

;; Find first map of next maps in languages.
(is (= {:language "Groovy" :site "https://www.groovy-lang.org" :dynamic true}
       (fnext langs)
       (first (next langs))))

;; Find next maps of next maps in languages.
(is (= (list {:language "Java" :site "https://www.java.com" :dynamic false}
             {:language "Kotlin" :site "https://kotlinlang.org" :dynamic false})
       (nnext langs)
       (next (next langs))))

Written with Clojure 1.10.1.

Original post written on April 13, 2020

Get Random Item From A Sequence

In Clojure we can use the rand-nth function to get a single random element from a sequence. To get multiple items based on random probability for each item we use the function random-sample. We must set the probability that determines for each item if it is in the result or not.

In the following example code we use rand-nth function:

(ns mrhaki.seq.random
  (:require [clojure.test :refer [is]]))

;; We use the function rand-nth to get a 
;; random element from a sequence collection.
(is (contains? #{"Clojure" "Java" "Groovy"} 
               (rand-nth ["Groovy", "Clojure", "Java"])))

;; We can use the rand-nth function with a map
;; if we first turn it into a sequence.
(is (contains? #{[:a 1] [:b 2]} (rand-nth (seq {:a 1 :b 2}))))

This next example shows how we can use the random-sample function:

(ns mrhaki.seq.random
  (:require [clojure.test :refer [is]]))

;; Using random-sample each item is in the
;; result based on the random probability of the
;; probability argument. 
;; When probability is 1 all items are returned.
(is (= ["Clojure" "Groovy" "Java"]
       (random-sample 1.0 ["Clojure" "Groovy" "Java"])))

;; When proability is 0 no item is in the result.
(is (empty? (random-sample 0 ["Clojure" "Groovy" "Java"])))

;; Any other value between 0 and 1 will return different
;; results for each invocation of the random-sample function.
(def samples (random-sample 0.4 ["Clojure" "Groovy"]))
(is (or (empty? samples)
        (= ["Clojure" "Groovy"] samples)
        (= ["Clojure"] samples)
        (= ["Groovy"] samples)))

Written with Clojure 1.10.1

Original post written on March 30, 2020

Query Set Of Maps With index Function

The namespace clojure.set has useful functions when we work with sets. One of the functions is the index function. The index function can be used for a set with map elements to create a new map based on distinct values for one or more keys. The first argument of the function is the set we transform and the second argument is a vector of one or more keys we want to index on. The keys in the new map are maps themselves. The value for each key is a set of maps that have the given keyword/value combination. The new map can be easily queried with the get function to get the values for a key.

In the next example code we see how we can use the clojure.set/index function to first transform a set with map elements to the new map and how to work with the resulting map:

(ns mrhaki.set.index
  (:require [clojure.test :refer [is]]
            [clojure.set :refer [index]]))

(def languages #{{:platform :jvm :name "Clojure"}
                 {:platform :jvm :name "Groovy"}
                 {:platform :native :name "Ruby"}
                 {:platform :jvm :name "JRuby"}
                 {:platform :native :name "Rust"}})

;; index function returns a map with a key for
;; each unique key/value combination for the keys
;; passed as second argument.
;; The value of each key is a set of the 
;; map that comply with the key/value combination.
(is (= {{:platform :jvm} #{{:platform :jvm :name "Clojure"}
                           {:platform :jvm :name "Groovy"}
                           {:platform :jvm :name "JRuby"}}
        {:platform :native} #{{:platform :native :name "Ruby"}
                              {:platform :native :name "Rust"}}}
        (index languages [:platform])))

;; We can use all collection functions on the map result
;; of the index function.
(is (= ["Clojure" "Groovy" "JRuby"]
       (map :name (get (index languages [:platform]) {:platform :jvm}))))


;; Set with sample data describing a shape
;; at a x and y location.
(def data #{{:shape :rectangle :x 100 :y 100}
            {:shape :circle :x 100 :y 100}
            {:shape :circle :x 100 :y 0}
            {:shape :circle :x 0 :y 100}})

;; We can use multiple keys as second argument of the
;; index function if we want to index on values of 
;; more thane one key.
(is (= {{:x 0 :y 100} #{{:shape :circle :x 0 :y 100}}
        {:x 100 :y 0} #{{:shape :circle :x 100 :y 0}}
        {:x 100 :y 100} #{{:shape :circle :x 100 :y 100}
                          {:shape :rectangle :x 100 :y 100}}}
       (index data [:x :y])))

(is (= #{{:shape :circle :x 100 :y 100}
         {:shape :rectangle :x 100 :y 100}}
         (get (index data [:x :y]) {:x 100 :y 100})))

Written with Clojure 1.10.1.

Original post written on July 23, 2020

Creating Union Of Sets

When we are working with sets in Clojure we can use some useful functions from the clojure.set namespace. In a previous post we learned how we can get the difference of several sets. To get the union of several input sets we use the union function of the clojure.set namespace. The function returns a new set that is the union of unique elements from the input sets. A nil value is ignored by the union function.

In the following example code we use union:

(ns mrhaki.set.union
  (:require [clojure.set :as set]
            [clojure.test :refer [is]]))

;; union return a set with elements that contains the unique
;; elements of the input sets.
(is (= #{"Java" "Clojure" "Groovy" "Kotlin"}
      (set/union #{"Java" "Clojure" "Groovy"} #{"Kotlin" "Groovy" "Clojure"})))

;; We can use multiple input sets.
(is (= #{"Java" "Clojure" "Groovy" "Kotlin"}
       (set/union #{"Java" "Clojure" "Groovy"} 
                  #{"Groovy" "Clojure"}
                  #{"Kotlin"})))

;; A nil input is ignored.
(is (= #{"Clojure" "Groovy" "Kotlin"}
       (set/union #{"Groovy" "Clojure"} nil #{"Kotlin"})))

Written with Clojure 1.10.1.

Original post written on July 2, 2020

Find Differences In Sets

If we want to get the values from a set that are not part of one or more other sets we must use the difference function in the clojure.set namespace. The function returns a set with all values from the first set that are different from values in other sets.

In the following example we use the difference with several sets:

(ns mrhaki.set.difference
  (:require [clojure.set :as set]
            [clojure.test :refer [is]]))

;; The difference function will take a first set 
;; and leave out elements that are in the following set(s).
(is (= #{"Java"}
       (set/difference #{"Java" "Clojure" "Groovy"} 
                       #{"Kotlin" "Groovy" "Clojure"})))

(is (= #{"Java"}
       (set/difference #{"Java" "Clojure" "Groovy"} 
                       #{"Kotlin" "Groovy"} 
                       #{"Clojure"})))


;; When other sets do not contain values
;; from the first set, the result is the original set.
(is (= #{1 2 3}
       (set/difference #{1 2 3} #{4 5})))

Written with Clojure 1.10.1.

Original post written on June 29, 2020

Getting But The Last Element(s) From A Collection

When we want to get elements from a collection, but not the last element(s), we can use the function butlast and drop-last. The function butlast returns all elements before the last element. If we use the function on an empty collection we get back a nil result. When we use the function drop-last we get back a lazy sequence. Also we can use an extra argument for the function to indicate how many of the last elements we don't want in the result. If we use drop-last on an emtpy collection we get back an empty lazy sequence.

In the following example we use both functions with several collections:

(ns mrhaki.core.butlast
  (:require [clojure.test :refer [is]]))

;; Sample vector with some JVM langauges.
(def languages ["Clojure" "Groovy" "Java"])

;; Sample map describing a user.
(def user {:alias "mrhaki" :first "Hubert" :last "Klein Ikkink" :country "The Netherlands"})

;; Using butlast to get all elements but 
;; not the last element as a sequence.
(is (= '("Clojure" "Groovy") (butlast languages)))

;; We can also use butlast on a map, the result
;; is a sequence with vectors containing the 
;; key/value pairs from the original map.
(is (= '([:alias "mrhaki"] [:first "Hubert"] [:last "Klein Ikkink"])
       (butlast user))
;; We can use the into function to transform this
;; into a map again.
    (= {:alias "mrhaki" :first "Hubert" :last "Klein Ikkink"}
       (into {} (butlast user))))

;; Returns nil when collection is empty.
(is (= nil (butlast [])))


;; drop-last returns a lazy sequence with all 
;; elements but the last element.
(is (= '("Clojure" "Groovy") (drop-last languages)))

;; Returns an empty sequence when collection is empty.
(is (= '() (drop-last [])))

;; We can use an extra argument with but-last
;; to indicate the number of items to drop
;; from the end of the collection.
;; butlast cannot do this.
(is (= ["Clojure"] (drop-last 2 languages)))

;; drop-last works on maps just like butlast.
(is (= '([:alias "mrhaki"]) (drop-last 3 user))
    (= {:alias "mrhaki"} (into {} (drop-last 3 user))))

Written with Clojure 1.11.1.

Original post written on October 30, 2022

Remove Duplicates From A Collection With distinct

With the function distinct we can remove duplicate elements from a collection. The function returns a lazy sequence when we use a collection argument. Without arguments the function returns a transducer. When we want to remove duplicates and we don't need the lazy sequence result we could also turn a collection into a set with for example the set or into functions.

In the following example we use the distinct function on several collections.

(ns mrhaki.core.distinct
  (:require [clojure.test :refer [is]]))

;; In the following example we have the results
;; from several throws with a dice and we want
;; to remove all duplicates.
(is (= [1 5 6 2 3] (distinct [1 5 5 6 2 3 3 1])))

;; Only duplicates are removed.
(is (= ["Clojure" "Groovy" "Java"]
       (distinct ["Clojure" "Groovy" "Java" "Java" "Java" "Clojure"])))

;; String is also a collection we can invoke distinct function on.
(is (= [\a \b \c \d \e \f] (distinct "aabccdeff")))

;; For example a collection of mouse clicks where
;; we want to get rid of duplicate clicks at the same position.
(is (= [{:x 1 :y 1} {:x 1 :y 2} {:x 0 :y 0}]
       (distinct '({:x 1 :y 1} {:x 1 :y 2} {:x 1 :y 1} {:x 0 :y 0}))))
       
;; When we don't need the sequence result with ordening we can
;; also use a set to remove duplicates. 
;; We loose the order of the elements.
(is (= #{1 5 6 2 3}
       (set [1 5 6 5 2 3 1])
       (into #{} [1 5 6 5 2 3 1])))

Written with Clojure 1.10.1.

Original post written on February 8, 2021

Remove Consecutive Duplicate Elements From Collection

The Clojure core namespace contains many functions. One of the functions is the dedupe function. This function can remove consecutive duplicates from a collection and returns a lazy sequence where only one of the duplicates remain. It will not remove all duplicate elements from the collection, but only when the element is directly followed by a duplicate element. The function returns a transducer when no argument is given.

In the following code sample we use the dedupe function on several collections:

(ns mrhaki.core.dedupe
  (:require [clojure.test :refer [is]]))

;; In the following example we have the results
;; from several throws with a dice and we want
;; remove duplicates that are thrown after another.
(is (= [1 5 6 2 3 1] (dedupe [1 5 5 6 2 3 3 1])))

;; Only consecutive duplicates are removed.
(is (= ["Clojure" "Groovy" "Java" "Clojure"]
       (dedupe ["Clojure" "Groovy" "Java" "Java" "Java" "Clojure"])))

;; String is also a collection.
(is (= [\a \b \c \d \e \f] (dedupe "aabccdeff")))

;; For example a collection of mouse clicks where
;; we want to get rid of consecutive clicks at the same position.
(is (= [{:x 1 :y 2} {:x 1 :y 1} {:x 0 :y 0}] 
       (dedupe '({:x 1 :y 2} {:x 1 :y 1} {:x 1 :y 1} {:x 0 :y 0}))))

Written with Clojure 1.10.1.

Original post written on February 5, 2021

Getting Part Of A Vector With subvec

In Clojure we can get part of a vector collection using the subvec function. The function takes a vector as argument, a required begin index and optional end index. The returned value is a vector with part of the values of the original vector starting from the begin up to the end index. If we leave out the optional end index, the size of the vector is used as end index.

In the following example we use the subvec function with and without the end index:

(ns mrhaki.core.subvec
  (:require [clojure.test :refer [is]]))

;; Vector of some JVM languages.
(def languages ["Java" "Kotlin" "Clojure" "Groovy"])

;; Using only the start index argumnt we get all items
;; from the start index to the end.
(is (= ["Clojure" "Groovy"] (subvec languages 2)))

;; When we use the start and end index arguments
;; we get the items from start to the given end.
(is (= ["Clojure"] (subvec languages 2 3)))

Written with Clojure 1.10.1.

Original post written on November 2, 2020

Shuffle A Collection

In Clojure we can use the shuffle function with a collection argument to get a new collection where the items of the input collection are re-ordered randomly. The function delegates to the Java java.util.Collections#shuffle method.

In the following example code we use the shuffle method:

(ns mrhaki.core.shuffle
  (:require [clojure.test :refer [is]]))

;; shuffle will return a new collection 
;; where the items are in a different order.
(shuffle (range 5)) ;; Possible collection [4 0 1 2 3]
(shuffle (range 5)) ;; Possible collection [1 3 4 2 0]

;; Define a deck of cards.
(def cards (for [suite  [\♥ \♠ \♣ \♦]
                 symbol (concat (range 2 11) [\J \Q \K \A])] 
             (str suite symbol)))

;; Some checks on our deck of cards.
(is (= 52 (count cards)))
(is (= (list "♥2" "♥3" "♥4" "♥5" "♥6" "♥7" "♥8" "♥9" "♥10" "♥J" "♥Q" "♥K" "♥A")
       (take 13 cards)))

;; Let's shuffle the deck. We get a new collection of cards ordered randomly.
(def shuffled-deck (shuffle cards))

;; Shuffled deck contains all items from the cards collection.
(is (true? (every? (set cards) shuffled-deck)))

;; We can take a number of cards.
(take 5 shuffled-deck) ;; Possible result: ("♦6" "♦10" "♥K" "♥4" "♥10")

;; We do a re-shuffle and get different cards now.
(take 5 (shuffle shuffled-deck)) ;; Possible result: ("♥10" "♥Q" "♦4" "♣8" "♠5")

Written with Clojure 1.10.1.

Original post written on October 11, 2020

Finding The Maximum Or Minimum Value

To find the maximum or minimum value for numeric values we can use the max and min function. The functions accept one or more numeric arguments and the value that is maximum or minimum is returned. If the numbers are already in a sequence we can use apply max or apply min. If the values are not numbers we can use the max-key or min-key functions. These functions take as first argument a function that returns a number. So we can get the value that has the maximum or minimum return value for the function we pass as first argument.

In the next exmaple code we use the max, min, max-key and min-key functions:

(ns mrhaki.core.min-max
  (:require [clojure.test :refer [is]]))

;; We can use max to find the maximum number in the given arguments.
(is (= 20 (max 10 2 3 1 20)))

;; If we have a collection we can use apply max to find the maximum number.
(is (= 20 (apply max [10 2 3 1 20])))


;; We can use min to find the minimum number in the given arguments.
(is (= 1 (min 10 2 3 1 20)))

;; And also use apply min when we have collection with numbers.
(is (= 1 (apply min [10 2 3 1 20])))


;; When the arguments are not numbers we can provide a function to get
;; back numbers and use that function with max-key to find the maximum.
(is (= "Clojure" (max-key count "Java" "Groovy" "Clojure")))
(is (= "Clojure" (apply max-key count ["Java" "Groovy" "Clojure"])))


;; And to find the minimum for non-numbered arguments we can use min-key
;; with a function to get back numbers.
(is (= "Java" (min-key count "Java" "Groovy" "Clojure")))
(is (= "Java" (apply min-key count ["Java" "Groovy" "Clojure"])))

Written with Clojure 1.10.1.

Original post written on October 6, 2020

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.

Original post written on July 15, 2020

Partition Collection Into Sequences

Clojure has the partition, partition-all and partition-by functions to transform a collection into a list of sequences with a (fixed) number of items. We can set the number of items in each sequence by providing a number as the first argument of the partition and partition-all functions. Any remainder elements are not in the resulting list of sequences when we use partition, but are when we use partition-all. We can also specify another collection to use values from to fill up the remainder as the third argument of the partition function.\ Optionally we can specify an offset step value as a second argument using both functions. This mean a new partition sequence will start based on stepping through the original collection with the given step value.\ Finally we can use a function to define when a new partition must start with the partition-by function. Every time the function returns a new value a new partition will begin.

In the following example Clojure code we use all three functions with all possible arguments:

(ns mrhaki.core.partition
  (:require [clojure.test :refer [is]]))

;; Sample string (a sequence of characters).
(def letters "aBCdeFg")

;; First argument defines how many items are in each partition.
;; Any remainder is ignored. 
(is (= [[\a \B] [\C \d] [\e \F]] (partition 2 letters)))

;; With partition-all the remainder is part of the result.
(is (= [[\a \B] [\C \d] [\e \F] [\g]] (partition-all 2 letters)))


;; The second argument is a step offset.
(is (= [[\a \B] [\d \e]] (partition 2 3 letters)))

(is (= [[\a \B] [\d \e] [\g]] (partition-all 2 3 letters)))

(is (= [[\a \B \C] [\C \d \e] [\e \F \g]] (partition 3 2 letters)))

(is (= [[\a \B \C] [\C \d \e] [\e \F \g] [\g]] (partition-all 3 2 letters)))


;; The third argument is used to fill the last remainder partition if needed.
(is (= [[\a \B \C] [\d \e \F] [\g \! \?]] (partition 3 3 [\! \? \@] letters)))

(is (= [[\a \B \C] [\d \e \F] [\g \! \!]] (partition 3 3 (repeat \!) letters)))

;; When padding collection has not enough items, only what is available
;; is used to fill the remainder part.
(is (= [[\a \B \C] [\d \e \F] [\g \!]] (partition 3 3 [\!] letters)))


;; Using partition-by we can use a function that perfoms the split
;; when the function returns a new value.
(is (= [[\a] [\B \C] [\d \e] [\F] [\g]]
       (partition-by #(Character/isUpperCase %) letters)))

(is (= [[ 1 2 3 4] [5] [6 7 8 9] [10] [11 12 13 14]]
       (partition-by #(= 0 (mod % 5)) (range 1 15))))

Written with Clojure 1.10.1.

Original post written on April 28, 2020

Counting Frequency Of Items In A Collection

If we want to know how often an item is part of a collection we can use the frequencies function. This function returns a map where each entry has the item as key and the number of times it appears in the list as value.

In the following example Clojure code we use the frequencies function:

(ns mrhaki.core.frequencies
  (:require [clojure.test :refer [are is]]))


(def sample "Clojure is cool!")

(is (= {\space 2 \! 1 \C 1 \c 1 \e 1 \i 1 \j 1 \l 2 \o 3 \r 1 \s 1 \u 1}
       (frequencies sample))
    "Frequency of each character in sample")


(def list ["Clojure" "Groovy" "Cool" "Goodness"])

(is (= {\C 2 \G 2}
       (frequencies (map first list)))
    "Two words start with C and two with G")


(def numbers '(29 31 42 12 8 73 46))

(defn even-or-odd
  "Return string even when number is even, 
   otherwise return string odd."
  [n]
  (if (even? n)
    "even"
    "odd"))

(is (= {"odd" 3 "even" 4}
       (frequencies (map even-or-odd numbers)))
    "list numbers has 3 odd and 4 even numbers")


(def user {:user "mrhaki" :city "Tilburg" :age 46})

(is (= {java.lang.String 2 java.lang.Long 1}
       (frequencies (map type (vals user))))
    "user map has two values of type String and 1 of type Long")

Written with Clojure 1.10.1.

Original post written on April 24, 2020

Reapply Function With Iterate To Create Infinite Sequence

The iterate function create a lazy, infinite sequence based on function calls. The iterate function takes a function and an initial value as arguments. The first element in the sequence is the initial value, next the function is invoked with the previous element as argument and this continues for each new element. Suppose we have a function #(+ 2 %) that adds 2 to the input argument. Then if we use this function with iterate and start with value 1 the first elements of the sequence will be 1, (+ 2 1), (+ 2 3), (+ 2 5). So first element is the initial value, next element is the invocation of the function with input argument 1. The result of this function is 3, which is then the input for the function to calculate the next element and so on.

In the following example code we use iterate in different scenario's:

(ns mrhaki.core.iterate
  (:require [clojure.test :refer [is]]))

;; Lazy sequence of numbers in steps of 2.
(def odds (iterate #(+ 2 %) 1))

(is (= (list 1 3 5 7 9 11 13 15 17 19)
       (take 10 odds)))


;; Define lazy sequence with a growing string.
;; The first element is ar, next argh, then arghgh etc.
(def pirate (iterate #(str % "gh") "ar"))

(def crumpy-pirate (nth pirate 5))

(is (= "arghghghghgh" crumpy-pirate))


;; Function that returns the given amount
;; plus interest of 1.25%.
(defn cumulative-interest
  [amount]
  (+ amount (* 0.0125 amount)))

;; Lazy sequence where each entry is the 
;; cumulative amount with interest based 
;; on the previous entry. 
;; We start our savings at 500.
(def savings (iterate cumulative-interest 500))

;; After 5 years we have:
(is (= 532.0410768127441
       (nth savings 5)))


;; Function to double a given integer
;; and return as bigint.
(defn doubler [n] (bigint (+ n n)))

;; Define lazy infinite sequence
;; where each element is the doubled value
;; of the previous element.
(def wheat-chessboard (iterate doubler 1))

;; First elements are still small.
(is (= (list 1 2 4 8 16 32)
       (take 6 wheat-chessboard)))

;; Later the elements grow much bigger.
(is (= (list 4611686018427387904N 9223372036854775808N)
       (->> wheat-chessboard (drop 62) (take 2))))

;; Sum of all values for all chessboard squares
;; is an impressive number.
(is (= 18446744073709551615N 
       (reduce + (take 64 wheat-chessboard))))

Written with Clojure 1.10.1.

Original post written on April 16, 2020

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.

Original post written on April 8, 2020

Keep Non-Nil Function Results From Collection

The keep function in Clojure invokes a function on each item in a collection and only returns non-nil results from the function invocation. The result of the keep function is a lazy sequence.

The following example uses the keep function, but also show what results would be when using map function on the same collection with the same function argument:

(ns mrhaki.seq.keep
  (:require [clojure.test :refer [is]]))

(def stuff ["Clojure" "Groovy" "Java"])

;; Function keep filters on non-nil results that are returned
;; from applying the function. 
(is (= '("Clojure has more than 6 characters")
       (keep #(if (> (count %) 6) (str % " has more than 6 characters")) stuff)))

;; Using the same function with map shows the
;; nil results that are filtered by keep.
(is (= (list "Clojure has more than 6 characters" nil nil)
       (map #(if (< 6 (count %)) (str % " has more than 6 characters")) stuff)))


(def user {:name "Hubert" :nickname "mrhaki" :age 46})

;; Returned result from the function is a boolean
;; so it is always included in the result after applying 
;; the keep function.
(is (= [true true false]
       (keep #(instance? String (% 1)) user)))

;; Here the function returns a string result or nill.
(is (= [":name has a String value" ":nickname has a String value"]
       (keep (fn [[k v]] (if (instance? String v) (str k " has a String value"))) user)))

Written with Clojure 1.10.1.

Original post written on April 2, 2020

Flatten Collections

We can use the flatten function when we have a collection with nested sequential collections as elements and create a new sequence with the elements from all nested collections.

In the following example we use the flatten function:

(ns mrhaki.sample
  (:require [clojure.test :refer [is]]))

;; Elements from nested sequential collections are flattend into new sequence.
(is (= [1 2 3 4 5] (flatten [1 [2 3] [[4]] 5])))
(is (sequential? (flatten [1 [2 3] [[4]] 5])))
(is (= [1 2 3 4 5] (flatten [[1] [2 3] [[4 5]]])))

;; We can use different sequential collection types.
;; We might have to force a type to a sequential collection with seq.
(is (= '(1 2 3 4 5) (flatten [1 (seq (java.util.List/of 2 3)) ['(4 5)]])))
(is (= (quote (1 2 3 4 5)) (flatten [[1] [(range 2 6)]])))

;; flatten on nil returns empty sequence.
(is (= () (flatten nil)))

Written with Clojure 1.10.1.

Original post written on January 10, 2020

Getting Intersections Between Sets

In the clojure.set namespace we can find the intersection function. This functions accepts one or more sets as arguments and return a new set with all elements that are present in the sets that are passed as arguments to the intersection function. The argument must be a set, so we need to convert other collection or seq values to a set first before we use it as an argument for the function.

In the following example we use one, two or three arguments for the intersection function and also convert other types to a set to be used as argument:

(ns mrhaki.sample
  (:require [clojure.set :refer [intersection]]
            [clojure.test :refer [is]]))

;; Use intersection with sets to find common elements.
(is (= #{"Clojure"} (intersection #{"Java" "Scala" "Clojure"} #{"Clojure" "Groovy"})))

;; An empty set is returned if there is no common element.
(is (= #{} (intersection #{"Java" "Groovy" "Clojure"} #{"C++" "C#"})))

;; We can use more than two sets to find intersections.
(is (= #{"Clojure"} (intersection #{"Java" "Scala" "Clojure"} 
                                  #{"Clojure" "Groovy"} 
                                  #{"Groovy" "JRuby" "Clojure"})))

;; With one set intersections returns the set.
(is (= #{"Clojure" "Groovy"} (intersection #{"Clojure" "Groovy"})))

;; Only sets are allowed as arguments for the intersection function.
;; If one of the arguments is not a set the return value is unexpected. 
(is (= #{} (intersection #{"Clojure" "Groovy"} ["Java" "Scala" "Clojure"])))
;; But we can convert a non-set to a set with the set function.
(is (= #{"Clojure"} (intersection #{"Clojure" "Groovy"} (set ["Java" "Scala" "Clojure"]))))
(is (= #{"Clojure"} (intersection #{"Clojure" "Groovy"} (set '("Java" "Scala" "Clojure")))))
;; Or using into #{}.
(is (= #{"Clojure"} (intersection #{"Clojure" "Groovy"} 
                                  (into #{} (vals {:platform "Java" :language "Clojure"})))))

Written with Clojure 1.10.1

Original post written on January 8, 2020

Files

Writing Text File Content With spit

In a previous post we learned how to read text file contents with the slurp function. To write text file content we use the spit function. We content is defined in the second argument of the function. The first argument allows several types that will turn into a Writer object used for writing the content to. For example a string argument is used as URI and if that is not valid as a file name of the file to read. A File instance can be used directly as argument as well. But also Writer, BufferedWriter, OutputStream, URI, URL and Socket. As an option we can specify the encoding used to write the file content using the :encoding keyword. The default encoding is UTF-8 if we don't specify the encoding option. With the option :append we can define if content needs to be appended to an existing file or the content should overwrite existing content in the file.

In the following example we use the spit function with several types for the first argument:

(ns mrhaki.sample.spit
  (:import (java.io FileWriter FilterWriter StringWriter File)))

;; With spit we can write string content to a file.
;; spit treats the first argument as file name if it is a string.
(spit "files/data/output.txt" (println-str "Clojure rocks!"))

;; We can add the option :append if we want to add text
;; to a file, instead of overwriting the content of a file.
(spit "files/data/output.txt" (println-str "And makes the JVM functional.") :append true)

;; Another option is the :encoding option which is UTF-8 by default
(spit "files/data/output.txt" (str "Clojure rocks!") :encoding "UTF-8")

;; The first argument can also be a File.
(spit (File. "files/data/file.xt") "Sample")

;; We can pass a writer we create ourselves as well.
(spit (FileWriter. "files/data/output.txt" true) "Clojure rocks")

;; Or use a URL or URI instance.
(spit (new java.net.URL "file:files/data/url.txt") "So many options...")
(spit (java.net.URI/create "file:files/data/url.txt") "And they all work!" :append true)

Written with Clojure 1.11.1.

Original post written on October 1, 2022

Reading Text File Content With slurp

The slurp funtion in Clojure can be used to read the contents of a file and return it as a string value. We can use several types as argument for the function. For example a string argument is used as URI and if that is not valid as a file name of the file to read. A File instance can be used directly as argument as well. But also Reader, BufferedReader, InputStream, URI, URL, Socket, byte[] and char[]. As an option we can specify the encoding used to read the file content using the :encoding keyword. The default encoding is UTF-8 if we don't specify the encoding option.

In the following example we use the slurp function in different use cases. We use a file named README with the content Clojure rocks!:

(ns mrhaki.sample.slurp
    (:require [clojure.java.io :as io]
              [clojure.test :refer [is]])
    (:import (java.io File)))
  
  ;; slurp interperts a String value as a file name.
  (is (= "Clojure rocks!" (slurp "files/README")))
  ;; Using the encoding option.
  (is (= "Clojure rocks!" (slurp "files/README" :encoding "UTF-8")))

  ;; We can also use an explicit File object.
  (is (= "Clojure rocks!" (slurp (io/file "files/README"))))
  (is (= "Clojure rocks!" (slurp (File. "files/README"))))
  
  ;; We can also use an URL as argument.
  ;; For example to read from the classpath:
  (is (= "Clojure rocks!" (slurp (io/resource "data/README"))))
  
  ;; Or HTTP endpoint
  (is (= "Clojure rocks!" (slurp "https://www.mrhaki.com/clojure.txt")))

Written with Clojure 1.11.1.

Original post written on September 27, 2022

Create All Parent Directories For A File

The Clojure namespace clojure.java.io contains useful functions to work with files. One of those functions is make-parents. We can pass a java.io.File instance as arguments or use the same arguments that can be passed to the file function that is also in this namespace. The function will create all parent directories for the file. The return result is true if the directories are created (they didn't exist before) and false when the directories didn't have to be created (already exist).

In the following example we see an example of usage of the make-parents function:

(ns mrhaki.io.make-parents
  (:require [clojure.java.io :refer [make-parents file]]
            [clojure.test :refer [is]]))

;; make-parents will create the parents directories for a file 
;; The function returns true if the directories are created, 
;; false if the directories already exist.
(let [file (file "tmp" "clojure" "sample.txt")]
  (is (true? (make-parents file)) "All parent directories are created")
  (is (false? (make-parents file)) "Second time the directory already exists"))

;; make-parents uses the same arguments as the clojure.java.io.file function
(is (true? (make-parents "tmp" "clj" "sample.txt")) "Directories tmp/clj are created")

Written with Clojure 1.10.3.

Original post written on September 27, 2021

Java Interoperability

Create New Instance Of Java Class

Working with Java classes from Clojure code is easy. If we want to invoke methods or access fields on instances of Java classes we must first create an instance by invoking the constructor. In Clojure we can do that using the special form new or using a dot (.) notation. The new special form has the class name of the Java class we want to create an instance of as argument followed by arguments for the Java class constructor. With the dot notation we place a . after the class name followed by arguments for the class constructor to create a new instance.

In the following example we see several ways to create an instance of a Java class.

(ns mrhaki.java.new-instance
  (:require [clojure.test :refer [is]])
  (:import (java.net URI)
           (java.util Map TreeMap)))

;; Using a dot after the class name to invoke the constructor.
(is (instance? String (String.)))

;; Or using the new special form to invoke the constructor.
(is (instance? String (new String)))

;; Constructor arguments can be used.
(is (instance? URI (URI. "https://www.mrhaki.com")))
(is (instance? URI (new URI "https" "www.mrhaki.com" "/" "")))

;; We can use Clojure data structures in constructors.
(is (instance? Map (TreeMap. {:language "Clojure"})))

Written with Clojure 1.10.1.

Original post written on March 18, 2021

Create And Initialize Object Based On Java Class With doto

It is very easy to work with Java classes in Clojure. If we want to create a new object based on a Java class and invoke methods to initialize the object directly we can use the doto macro. The first argument is an expression to create a new object and the rest of the arguments are functions to invoke methods on the newly created object. The object returned from the first argument is passed as first argument to the method invocations. The doto function returns the object that is created with the first argument.

In the following example code we use the doto function in several cases:

(ns mrhaki.core.doto
  (:require [clojure.test :refer [is]]))

;; With doto we can invoke functions on object returned
;; by the first argument, where the object is passed
;; before the given arguments.
(def sb (doto (StringBuilder.)
          (.append "one")
          (.append "two")
          (.reverse)))

(is (= "owteno" (.toString sb)))

;; We can use functions in doto. For example
;; to create a value for the function invocation
;; on the type of the first argument.
(def sample (doto (new StringBuilder)
              (.append "{")
              (.append (apply str (repeat 10 "a")))
              (.append "}")))

(is (= "{aaaaaaaaaa}" (.toString sample)))

;; Type returned is the same as result of evaluation
;; of first argument, not the last argument.
(is (instance? StringBuilder (doto (StringBuilder.) (.toString))))

Written with Clojure 1.10.1.

Original post written on July 13, 2020

Use .. For Invocation Java Method Chaining

Accessing Java from Clojure is easy. With the dot (.) special form we can invoke for example methods from a Java class or instance. If we want to invoke several methods together where the return value from one method is used to invoke the next method (method chaining) we can use the .. macro. The macro will expand into a nested expression with the . forms.

In the following example we see how to use the .. macro and how we can achieve the same result using nested . expressions and by using the thread first macro:

(ns mrhaki.java
  (:require [clojure.test :refer [is]])
  (:import (java.util Optional)))

(def value "Clojure")

;; We use Optional map method that accepts a java.util.function.Function,
;; so here we implement the Function interface with an implementation
;; to return the given String value in upper case.
(def fn-upper
  (reify java.util.function.Function
    (apply [this arg] (. arg toUpperCase))))

(is (= "CLOJURE"
       ;; Invoke Java method chaining using the special .. macro.
       ;; Java: Optional.ofNullable(value).map(s -> s.toUpperCase()).orElse("Default")
       (.. Optional (ofNullable value) (map fn-upper) (orElse "Default"))
       
       ;; Macro expands to the following equivalent using . form.
       (. (. (. Optional ofNullable value) (map fn-upper)) (orElse "Default"))
       
       ;; Using thread first macro with equivalent method invocations.
       (-> (Optional/ofNullable value)
           (. (map fn-upper))
           (. (orElse "Default")))))

(is (= "Default"
       (.. Optional (ofNullable nil) (map fn-upper) (orElse "Default"))))

Written with Clojure 1.10.1.

Original post written on February 3, 2021

Invoke Java Method With Varargs Parameter

Sometimes we want to invoke Java methods from our Clojure code. If the Java method accepts a variable arguments (varargs) parameter and we want to invoke the method from Clojure we must pass an array as argument. To create an array in Clojure we can use several functions. The to-array function will transform a collection to an Object[] type. For primitive type arrays we can use for example int-array to get a int[] array. The function into-array is the most flexible function. This function accepts a sequence argument and optionally the class type of the resulting array. Once we have the array we can use it as argument value for the varargs parameter of the Java method we want to invoke.

In the following example we use into-array, to-array and short-array to invoke a Java method with varargs parameter and see how we can build different array types:

(ns mrhaki.core.varargs
  (:require [clojure.test :refer [is]])
  (:import (java.text MessageFormat)))

;; We want to invoke the Java method MessageFormat/format that accepts
;; a format parameter followed by a varargs parameter.
(is (thrown-with-msg? ClassCastException 
                      #"java.lang.String incompatible with \[Ljava.lang.Object;" 
                      (MessageFormat/format "{0} is awesome." "Clojure")))
                      
;; Use into-array to transform sequence to Java array,
;; that can be used for methods that accept varargs parameter.
(is (= "Clojure is awesome."
       (MessageFormat/format "{0} is awesome."
                             (into-array ["Clojure"]))))

;; In the next example the type of the array is based on the first element
;; and becomes String[], but the sequence has also elements with other types.
;; We use the argment Object to have an Object[] array.
(is (= "Clojure contains 7 characters."
       (MessageFormat/format "{0} contains {1} characters."
                             (into-array Object ["Clojure" (count "Clojure")]))))

;; To get an Object[] array we could also use to-array function 
;; with a collection argument.
(is (= "Clojure contains 7 characters."
       (MessageFormat/format "{0} contains {1} characters."
                             (to-array ["Clojure" (count "Clojure")]))))

;; Type of first element sets array type.
(is (= "[Ljava.lang.String;"
       (.getName (class (into-array ["Clojure" "Groovy"])))))

;; Use explicit type or to-array function that always returns Object[] array.
(is (= "[Ljava.lang.Object;"
       (.getName (class (into-array Object ["Clojure" "Groovy"])))
       (.getName (class (to-array ["Clojure" "Groovy"])))))

;; Primitive types are transformed to array of boxed type: short, becomes Short.
(is (= "[Ljava.lang.Short;"
       (.getName (class (into-array (map short (range 5)))))))

;; We can get primitive type by using TYPE field of boxed type or
;; specific <primitive>-array function, like short-array.
(is (= "[S"
       (.getName (class (into-array Short/TYPE (map short (range 5)))))
       (.getName (class (short-array (range 5))))))

Written with Clojure 1.10.1.

Original post written on January 28, 2021

Turn Java Object To Map With bean Function

The map data structure is used a lot in Clojure. When we want to use Java objects in our Clojure code we can convert the Java object to a map with the bean function. This function will use reflection to get all the properties of the Java object and converts each property with the property value to a key with value in the resulting map. The bean function will not recursively convert nested objects to a map, only the top-level properties are turned into key value pairs.

We see several examples of using the bean function in the following code snippet:

(ns mrhaki.core.bean
  (:require [clojure.test :refer [is]])
  (:import (java.net URI)
           (mrhaki.java Person Address)))

(is (= {:path "",
        :rawQuery "search=clojure",
        :fragment nil,
        :authority "www.mrhaki.com",
        :rawAuthority "www.mrhaki.com",
        :port -1,
        :absolute true,
        :host "www.mrhaki.com",
        :rawPath "",
        :opaque false,
        :rawSchemeSpecificPart "//www.mrhaki.com?search=clojure",
        :class java.net.URI,
        :rawUserInfo nil,
        :query "search=clojure",
        :rawFragment nil,
        :scheme "https",
        :userInfo nil,
        :schemeSpecificPart "//www.mrhaki.com?search=clojure"}
       (bean (URI. "https://www.mrhaki.com?search=clojure"))))


(comment "For the next sample we use a Java class Person
  with properties name and alias of type String and
  a third property of type Address. The Java class Address
  has a single String property city.
  
  Pseudo code: 
  class Person { String name, alias; Address address; }
  class Address { String city; }")

(def person (Person. "Hubert" "mrhaki" (Address. "Tilburg")))

(is (= {:name "Hubert" :alias "mrhaki"} 
       (select-keys (bean person) [:name :alias])))

;; Properties with custom classes are not automatically
;; also converted to a map representation.
(is (instance? mrhaki.java.Address (:address (bean person))))

(is (= {:city "Tilburg"}
       (select-keys (bean (:address (bean person))) [:city])))


(is (= {:address {:city "Tilburg", :class mrhaki.java.Address},
        :alias "mrhaki",
        :class mrhaki.java.Person,
        :name "Hubert"}
       (assoc (bean person) :address (bean (.getAddress person)))))

Written with Clojure 1.10.1.

Original post written on July 14, 2020