如何实现从值到关键字的数据转换?(Clojure映射场景)
问题
现有如下Clojure映射数据:
({: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})
需要实现两种聚合转换:
- 按
type字段聚合:将type的取值转为关键字,同年月的金额合并,缺失的类型金额填0({:year 2023, :month 4, :typeA 62.5, :typeB 45.25, :typeC 40.0} {:year 2023, :month 5, :typeA 52.75, :typeB 0, :typeC 14.0}) - 按
cat字段聚合:逻辑同上,结果如下({:year 2023, :month 4, :catA 102.5, :catB 45.25} {:year 2023, :month 5, :catA 66.75, :catB 0})
额外要求:type和cat字段需支持灵活扩展,不能硬编码字段名称。
解决方案
可以实现一个通用聚合函数,通过传入分组字段关键字(如:type或:cat)实现灵活扩展:
(defn aggregate-by-field [data group-field] ;; 提取分组字段的所有唯一取值,并转为对应关键字(例:TypeA → :typeA) (let [all-values (->> data (map group-field) distinct (map #(keyword (name %)))) ;; 按年月分组,计算每组内各取值的金额总和 grouped (->> data (group-by (juxt :year :month)) (map (fn [[[year month] items]] (merge {:year year :month month} (reduce (fn [acc item] (let [k (keyword (name (group-field item))) amt (:amount item)] (update acc k (fnil + 0) amt))) {} items)))))] ;; 补全每组缺失的取值,金额设为0 (map #(merge (zipmap all-values (repeat 0)) %) grouped)))
使用示例
(def sample-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})) ;; 按type聚合 (aggregate-by-field sample-data :type) ;; 按cat聚合 (aggregate-by-field sample-data :cat)
代码说明
- 提取唯一取值:从全量数据中取出指定分组字段的所有值,去重后转为对应的关键字,确保覆盖所有可能的分组项。
- 年月分组求和:用
group-by按年月组合分组,对每组内的项按分组字段累加金额,生成基础聚合结果。 - 补全缺失项:生成包含所有分组项、金额为0的映射,与每组聚合结果合并,保证所有分组项都存在,缺失项金额自动补0。
该实现无需硬编码任何分组字段的具体取值,新增其他分组字段时直接传入对应关键字即可。
内容的提问来源于stack exchange,提问作者triplem
相关产品推荐
相关产品推荐

