Avi Drucker / May 02 2021
Remix of Clojure by
Nextjournal
Clojure Koans 22 Group-By Notebook
Original Clojure Koans: https://github.com/functional-koans/clojure-koans/
Spoilers Alert
{:deps {org.clojure/clojure {:mvn/version "1.10.1"} ;; complient is used for autocompletion ;; add your libs here (and restart the runtime to pick up changes) compliment/compliment {:mvn/version "0.3.9"}}}deps.edn
Extensible Data Notation
{:hello (clojure-version)}0.1s
Clojure
Initial Thoughts
This was the "missing piece" for me from yesterday's notebook :)
(defn get-odds-and-evens [coll] (let [{odds true evens false} (group-by odd? coll)] [odds evens]))Clojure
;; "To categorize a collection by some function, use group-by."(= {5 ["hello" "world"] 3 ["foo" "bar"]} (group-by count ["hello" "world" "foo" "bar"]));; description: Group strings of a vector by their length into a hashmap where the key:value pairs are word-length to vector-of-words;; exercise 22001: Create a map of words by length where the key is the word length and the value is all words of said length from the orgiginal collection;; skill 22001: Bucket items from a collection into a hashmap by means of a predefined function and said collection to the `group-by` functionClojure
;; "You can simulate filter + remove in one pass"(= (get-odds-and-evens [1 2 3 4 5]) ((juxt filter remove) odd? [1 2 3 4 5]) [[1 3 5] [2 4]]);; question 22001: What does the `juxt` function do?;; answer 22001a: From the docs:;; "Takes a set of functions and returns a fn that is the juxtaposition;; of those fns. The returned fn takes a variable number of args, and;; returns a vector containing the result of applying each fn to the;; args (left-to-right).;; ((juxt a b c) x) => [(a x) (b x) (c x)]";; question 22002: What does the `remove` function do?;; answer 22002a: From the docs:;; "Returns a lazy sequence of the items in coll for which;; (pred item) returns logical false. pred must be free of side-effects.;; Returns a transducer when no collection is provided."Clojure
;; "You can also group by a primary key"(= {1 [{:id 1 :name "Bob"} {:id 1 :last-name "Smith"}] 2 [{:id 2 :name "Jennifer"}]} (group-by :id [{:id 1 :name "Bob"} {:id 2 :name "Jennifer"} {:id 1 :last-name "Smith"}]));; skill 22002: Bucket items from a collection of hashmaps into a hashmap by passing a primary "matching key".Clojure
;; "But be careful when you group by a non-required key"(= {"Bob" [{:name "Bob" :id 1}] "Jennifer" [{:name "Jennifer" :id 2}] nil [{:last-name "Smith" :id 1}]} (group-by :name [{:id 1 :name "Bob"} {:id 2 :name "Jennifer"} {:id 1 :last-name "Smith"}]));; skill 22003: Bucket items from a collection of hashmaps by passing a non-required (non-primary) "matching key" into a hashmap.;; question 2203: What is an example of the follow: the "gotcha" when using `group-by` on a collection where the search key is non-existant (it will group under `nil` instead)Clojure
;; "The true power of group-by comes with custom functions"(= {:naughty-list [{:name "Jimmy" :bad true} {:name "Joe" :bad true}] :nice-list [{:name "Jack" :bad false}]} (group-by (if (:bad %) :naughty-list :nice-list) [{:name "Jimmy" :bad true} {:name "Jane" :bad false} {:name "Joe" :bad true}]));; skill 22004: Bucket items from a collection into a hashmap by a custom predicate function mapped over said collection.Clojure
Closing Thoughts
I'd like to have simpler and clearer language to describe what I am doing with group by. Also, what is another way to say predicate? To-do's for next time:
[ ] Read: https://www.tutorialspoint.com/clojure/clojure_predicates.htm
[ ] Read: https://practicalli.github.io/clojure/clojure-spec/data/predicate-specifications.html
[ ] Read: https://www.cognitect.com/blog/2016/8/9/focus-on-spec-predicates
[ ] Read: https://danielcompton.net/2017/03/31/clojure-nil-predicates