Search

July 23, 2020

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

July 15, 2020

Clojure Goodness: Taking Or Dropping Elements From A Collection Based On Predicate

In Clojure we can take or drop elements from a collection based on a predicate using the functions take-while and drop-while. With the function take-while we take elements as long as the predicate returns true. Once the predicate returns false the function stops returning elements. Using the function drop-while we skip elements in the collection if the predicate returns true. If the predicate returns false the remaining elements in the collection are returned.

In the following example we use take-while and drop-while with different collection types:

(ns mrhaki.seq.take-while
  (:require [clojure.test :refer [is]]
            [clojure.string :refer [join]]))

;; Simple range of numbers to 10 to invoke
;; take-while and drop-while functions.
(def numbers (range 10))

;; Use take-while to get all number as long as
;; the number is less than 5.
(is (= [0 1 2 3 4] (take-while #(< % 5) numbers)))

;; Use drop-while to skip all numbers that are
;; less than 5, so we get all numbers from 5.
(is (= [5 6 7 8 9] (drop-while #(< % 5) numbers)))


;; String is a collection of characters so 
;; we can use take-while and drop-while.
(def s "Clojure Rocks!")

(is (= "Clojure "
       (join (take-while #(not= \R %) s))))

(is (= "Rocks!"
       (join (drop-while #(not= \R %) s))))


;; A map structure is a collection of key/value vectors, 
;; so take-while and drop-while can be used.
(def user {:name "mrhaki" :loves "Clojure" :worksAt "JDriven"})

;; Helper function to return the length of a keyword.
(defn keyword-length
  "Returns length of keyword."
  [entry] 
  (.length (name (key entry))))

(is (= {:name "mrhaki"}
       (into {} (take-while #(= 4 (keyword-length %)) user))))

(is (= {:loves "Clojure" :worksAt "JDriven"}
       (into {} (drop-while #(= (key %) :name) user))))

Written with Clojure 1.10.1.

July 14, 2020

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

July 13, 2020

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

July 8, 2020

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

July 5, 2020

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

July 2, 2020

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

June 29, 2020

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

June 25, 2020

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

June 19, 2020

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