Search

April 8, 2020

Clojure Goodness: Checking Predicate For Every Or Any Element In A Collection

In Clojure we can use several functions to see if at least one or all elements in a collection return true or false for a predicate. The function every? only returns true if the predicate function returns true for all elements in the collection. To function not-every? return true if a predicate function return false for all elements in a collection. The some function is a bit different (notice there is no ?) and returns the first logical true value from the predicate function for the elements in the collection. So the return type of the predicate doesn't have to be a Boolean value and then the return type of some is also not a Boolean. If the predicate returns a Boolean value we can use some like a any function (any is not part of Clojure). Clojure provides a not-any? function that returns true if the predicate function returns false for one element in the collection and false otherwise.

The following example uses the different functions on a vector with some cartoon names:

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

;; Vector of toons to check predicates on.
(def toons ["Daffy Duck" "Bugs Bunny" "Elmer Fudd" "Yosemite Sam"])

;; Helper function to count number of names.
(defn count-names
  [name]
  (count (str/split name #" ")))

;; Every toon has two names.
(is (true? (every? #(= 2 (count-names %)) toons)))

;; Not every toon name starts with "A".
(is (true? (not-every? #(str/starts-with? "A" %) toons)))


;; Helper function to check if the first letter
;; of both names is the same.
(defn same-first-letters?
  [name]
  (let [names (str/split name #" ")
        first-letter (first (first names))
        second-letter (first (second names))]
    (= first-letter second-letter)))

;; Some toons have the same first letter
;; for their first and last name.
(is (true? (some same-first-letters? toons)))

;; Using set as function to check toon is in the toons vector.
;; Notice some function return the first value from the predicate function
;; that is not nil or false, instead of a boolean like with
;; every?, not-every? and not-any?.
(is (not (true? (some #{"Yosemite Sam", "Road Runner"} toons))))
(is (= "Yosemite Sam" (some #{"Yosemite Sam", "Road Runner"} toons)))

;; As seen on https://clojuredocs.org/clojure.core/any_q a
;; possible implementation for any that returns true or false.
(defn any [pred coll] ((comp boolean some) pred coll))
(is (true? (any #{"Yosemite Sam", "Road Runner"} toons)))

;; There is a toon name where their name length is 10.
(is (false? (not-any? #(= (count %) 10) toons)))

Written with Clojure 1.10.1.

April 6, 2020

Clojure Goodness: 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.

April 2, 2020

Clojure Goodness: 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.

April 1, 2020

Clojure Goodness: 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.

March 30, 2020

Clojure Goodness: 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

March 29, 2020

Clojure Goodness: 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 %))))

(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.

March 11, 2020

Groovy Goodness: Getting Parts Of A String Enclosed By Strings

Groovy 3 adds the takeBetween method to the String class. With this method we can get all the characters that are enclosed by string values. We can specify one enclosed string value and then all text between the the first occurrence of the string and the second occurrence is returned. If multiple parts are enclosed by the string values we can also specify which occurrence we want. If the text is enclosed by different string values we can use a variant of takeBetween that takes two string values to indicate the boundaries of the text we want. Also with two different enclosed string values we can use an argument to get the n-th occurrence of the string that is found.
Since Groovy 3 we can also use takeBefore and takeAfter to get the string before or after a given string value. All three methods will return an empty string if no text can be found.

In the following example we use the takeBefore, takeAfter and takeBetween methods with different arguments:

def text = 'Just saying: "Groovy is gr8!"'

// Return all characters before the first quote.
assert text.takeBefore('"') == 'Just saying: '
// Return everything after the colon.
assert text.takeAfter(': ') == '"Groovy is gr8!"'
// Return everything between two quotes.
assert text.takeBetween('"') == 'Groovy is gr8!'
// Return text between is and !.
assert text.takeBetween('is', '!') == ' gr8'

// When no value can be found
// an empty string is returned.
assert text.takeBefore('?') == ''
assert text.takeAfter('Java') == ''
assert text.takeBetween('-') == ''
assert text.takeBetween('[', '/') == ''

def sample = 'JVM languages are "Groovy", "Clojure", "Java".'

assert sample.takeBetween('"') == 'Groovy'
// We can also specify which occurrence we 
// want for a text between same strings.
assert sample.takeBetween('"', 0) == 'Groovy'
assert sample.takeBetween('"', 1) == 'Clojure'
assert sample.takeBetween('"', 2) == 'Java'


def users = "Users: [mrhaki], [hubert]"

assert users.takeBetween('[', ']') == 'mrhaki'
// We can also specify which occurrence we 
// want for a text between to strings.
assert users.takeBetween('[', ']', 0) == 'mrhaki'
assert users.takeBetween('[', ']', 1) == 'hubert'
// When no occurrence an empty string is returned.
assert users.takeBetween('[', ']', 2) == ''

Written with Groovy 3.0.2.

March 10, 2020

Groovy Goodness: Taking Or Dropping Number Of Characters From A String

Groovy adds a lot of methods to the Java String class. For example we can use the take method to get a certain number of characters from the start of a string value. With the drop method we remove a given number of characters from the start of the string. In Groovy 3 we can now also take and drop a certain number of characters from the end of a string using the methods takeRight and dropRight.

In the following example we see how we can use the methods:

def s = "Groovy rocks!"

// Drop first 7 characters.
assert s.drop(7) == "rocks!"

// Drop last 7 characters.
assert s.dropRight(7) == "Groovy"


// Take first 6 characters.
assert s.take(6) == "Groovy"

// Take last 6 characters.
assert s.takeRight(6) == "rocks!"

Written with Groovy 3.0.2.

March 8, 2020

Groovy Goodness: Check Object Instances Are The Same With === Operator

Groovy has used the == operator to check if objects are equal for a long time. To test if object instances are the same we must use the is method. Groovy 3 adds a new operator for the is method and that is the === operator. And to negate the result of the is method we can use the !== operator.

In the following example we use the === and !== operator to check if objects refer to the same instance or not:

def answer = new Integer(42)
def anotherAnswer = new Integer(42)
def meaningOfLife = answer

// Equals is true for answer and anotherAnswer
assert answer == anotherAnswer

// But they don't refer to the same instance.
assert answer !== anotherAnswer

// The variables meaningOfLife and answer
// do refer to the same object instance.
assert answer === meaningOfLife

Written with Groovy 3.0.2.

March 4, 2020

Groovy Goodness: Using !instanceof Operator

Groovy 3 adds the !instanceof operator to check if an object is not an instance of a type. This is a shorthand for using instanceof and then negate the result. It shows how little changes can make code easier to read.

In the following example we use the old way to check if object is not an instance of a type and the new !instanceof operator:

// Check using !(obj instanceof type) 
// if object is not an instance of type.
assert !("Groovy is awesome!" instanceof Number)

// Since Groovy 3 we can use !instanceof to check
// if object is not an instance of a type.
assert 42 !instanceof String

Written with Groovy 3.0.1.