Skip to content

Instantly share code, notes, and snippets.

@zachmandeville
Created August 10, 2021 04:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zachmandeville/3f4f805b5bc6dae3bd67c9899f48a832 to your computer and use it in GitHub Desktop.
Save zachmandeville/3f4f805b5bc6dae3bd67c9899f48a832 to your computer and use it in GitHub Desktop.
Clojure Refactor
;; Goal:
;; I have an array of data showing stores, and dollars spent at the store
(def input-data [{:month "06/21", :store "Mitesh", :total 5.9}
{:month "07/21", :store "Mitesh", :total 5.5}
{:month "07/21", :store "Moore Wilson", :total 85.16}
{:month "06/21", :store "Moore Wilson", :total 33.45}
{:month "07/21", :store "New World", :total 75.26}
{:month "06/21", :store "New World", :total 84.28}
{:month "06/21", :store "Pak n Save", :total 26}
{:month "07/21", :store "Pak n Save", :total 200.36}])
;; I want to transform it into a valid data object for a chart.js bar chart
;; Where the x-axis is months, y-axis is the dollar total, and the data is stacked by store.
;; the desired end result would look like so:
(def desired-data
{:datasets ({:label Pak n Save, :backgroundColor #BBAB8B, :data (26 200.36)}
{:label New World, :backgroundColor #EF8275, :data (84.28 75.26)}
{:label Moore Wilson, :backgroundColor #88A096, :data (33.45 85.16)}
{:label Mitesh, :backgroundColor #8A4F7D, :data (5.9 5.5)})
:labels (07/21 06/21)})
;; My current function groups the input data by store, then does a reduce to create
;; the proper datasets and labels maps. It feels wordy and a bit hard to follow now,
;; but i like that it is a single declarative function.
(let [store-colors ;; (the assignment of colour will be improved, but that's a separate topic)
{"Moore Wilson" "#88A096"
"Pak n Save" "#BBAB8B"
"Mitesh" "#8A4F7D"
"New World" "#EF8275"}]
(reduce-kv (fn [m k v]
(-> m (assoc :datasets (cons
(reduce (fn [acc {:keys [store]}]
(-> acc
(assoc :label store)
(assoc :backgroundColor (get store-colors store))
(assoc :data (map :total (sort-by :month v)))))
{} v)
(:datasets m)))
(assoc :labels (set (into (:labels m) (map :month v))))))
{:labels [] :datasets []}
(group-by :store input-data)))
;; I am curious: is there a simpler way to do this transformation?
;; Are there any "gotcha" logical holes in my function that could make the resulting data inaccurate?
(println "Thank you!")
@zachmandeville
Copy link
Author

With the help of Cora and SeanCorfield from the clojurian slack, this was refactored to:

(defn- format-dataset
  [data]
  (->> (group-by first (map (juxt :store :total) data))
       (reduce-kv (fn [acc k v]
                    (conj acc {:label k
                               :data (map second v)
                               :backgroundColor (store-color k)})) [])))

(def desired-data {:datasets (format-dataset input-data)
                                :labels (distinct (map :month input-data))})

My thanks to them!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment