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.