Search code examples
clojure

Data transformation trom value to keyword


Say I have the following data:

({:year 2023, :month 4, :type TypeA, :cat CatA, :amount 62.5} 
 {:year 2023, :month 4, :type TypeB, :cat CatB, :amount 45.25} 
 {:year 2023, :month 4, :type TypeC, :cat CatA, :amount 40.0} 
 {:year 2023, :month 5, :type TypeA, :cat CatA, :amount 52.75} 
 {:year 2023, :month 5, :type TypeC, :cat CatA, :amount 14.0})

I would like to transform this by type or by cat into the following:

({:year 2023, :month 4, :typeA 62.5, :typeB 42.25, :typeC 40.0}
 {:year 2023, :month 5, :typeA 52.75, :typeB 0, :typeC 14.0}}

or the same by cat:

({:year 2023, :month 4, :catA 102.5, :catB 42.25}
 {:year 2023, :month 5, :catA 52.75, :catB 14.0}}

I sorta tried it with distinct and nested maps but have not found a valid solution.

BONUS: The types and cats should be flexible and it is not safe to reference them by name hard-coded.


Solution

  • The result seems to be close enough.

    (let [data '({:year 2023, :month 4, :type TypeA, :cat CatA, :amount 62.5}
                 {:year 2023, :month 4, :type TypeB, :cat CatB, :amount 45.25}
                 {:year 2023, :month 4, :type TypeC, :cat CatA, :amount 40.0}
                 {:year 2023, :month 5, :type TypeA, :cat CatA, :amount 52.75}
                 {:year 2023, :month 5, :type TypeC, :cat CatA, :amount 14.0})
          key-fields [:year :month]
          k-field :cat
          ;k-field :type
          v-field :amount]
      (->> data
           (group-by #(select-keys % key-fields))
           (mapv (fn [[k ms]]
                   (reduce (fn [k m]
                             (let [;; Not sure if you need to make the first character lower-case here.
                                   f (keyword (k-field m))]
                               (assoc k f (v-field m))))
                           k ms)))))
    

    It doesn't add zeros for absent values, but I'd argue that it's usually best not to add them. Instead, the clients of this code can decide what to do with them.