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` function
Clojure
;; "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:

Runtimes (1)