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 expects a map as parameter. It extracts the values for the keys min and max and assigns them to variable named 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.