About Java and JRuby Development
JEE, Spring, Guice
Hibernate, Java Persistence (JPA)
and various Web Frameworks

Beautiful Clojure – Destructuring data

What is destructuring?

Destructuring is one of the beautiful features which impressed me, when learning Clojure. It helps you to assign individual elements of a vector/list or map to variables. You can destructure function parameters as well as return values.

A first example

Imagine a function which returns the age of the youngest and the oldest person in a sport club.

(defn find-min-max-age [club]
  (let [min-age (calculate-min-age club)
        max-age (calculate-max-age club)]
    [min-age max-age]))

The vector which is returned can be assigned directly to two variables: min and max.

(let [[min max] (find-min-max-age "Swim Club")]
  (println "Youngest member is " min)
  (println "Oldest member is " max))

I like this elegant approach a lot. It is much more compact as a comparable Java version, which requires additional code to deal with null values.

    Integer[] minMax = findMinMax("Swim Club");
    Integer min = minMax.length > 0 ? minMax[0] : null;
    Integer max = minMax.length > 1 ? minMax[1] : null;
    System.out.println("Youngest member is " + min);
    System.out.println("Oldest member is " + max);

More vector examples

You can destructure function parameters as well. It just works every where.

(defn find-team-member [[min max]] 
	(println min max))
	
; Calling the function
(find-team-member [5 20])

As well, you can ignore values. To ignore values at the end of the vector, just leave off the variables.

(let [[min] (find-min-max-age "Rugby Club")]
  (println "Youngest member is" min))

To ignore values at a specific place, just use _ as the variable name. It is a common convention.

(let [[_ max] (find-min-max-age "Kite Surfing Club")]
  (println "Oldest member is" max))

Destructuring maps

Clojure separates functions and data. Maps are frequently used to store data and they can be destructured as well.

The following function extracts the values for the keys min and max and assigns them to variable names min, max respectively.

(defn find-team-member[ {:keys [min max]} ] 
	(println min max))

; Let us use our function
(find-team-member {:min 5 :max 10})

The destructuring of a map is expressed as a map. What could be more concise?

If you need to get hold of the whole map, you can add a special key :as to the map.

(defn find-team-member[ {:keys [min max] :as all-data} ] 
	(println min max all-data))

; Let us use our function
(find-team-member {:min 5 :max 10 :foo "some value"})

Default values for maps

To specify default values for maps, just add a key :or and a map describing those values.

(defn find-team-member[ {:keys [min max] :or {min 3 max 20}}] 
	(println min max))

I hope you enjoyed the article.