Search

Dark theme | Light theme
Showing posts with label ClojureGoodness:Sets. Show all posts
Showing posts with label ClojureGoodness:Sets. Show all posts

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