Ruby中嵌套哈希按名称分组、日期聚合求和的实现问题
问题根因
你使用reduce(&:merge)合并哈希时,遇到相同的日期key会直接用后一个值覆盖前一个,不会做数值累加,因此得到的结果不符合预期。
方案1:纯Ruby实现现有数组聚合
不需要依赖额外库,直接对已生成的数组做处理即可得到你要的结果:
result = arr.group_by { |item| item[:name] }.map do |product_name, entries| # 初始化默认值为0的哈希,同日期数值累加 aggregated_data = entries.each_with_object(Hash.new(0)) do |entry, hash| entry[:data].each { |date, quantity| hash[date] += quantity } end { name: product_name, data: aggregated_data } end
方案2:支持多时间粒度的高性能实现
推荐直接在查询层做聚合,避免加载大量ActiveRecord对象,同时可以灵活切换按日/周/月分组:
# 可灵活调整的参数 start_date = Date.parse("Jan 1 2020") end_date = Date.parse("Feb 1 2020") group_period = :day # 需按周分组替换为:week,按月分组替换为:month result = LineItem.joins(:order, :product) .where(orders: { order_date: start_date..end_date }) .group("products.model_number") # 按指定粒度截断日期,格式化规则和你之前的写法保持一致 .group_by { |item| item.order.order_date.send("beginning_of_#{group_period}").strftime('%a %b %d, %Y') } .sum(:order_quantity) # 转换为Chartkick多系列要求的格式 .group_by { |(model, date), total| model } .map do |model, value_pairs| { name: model, data: value_pairs.to_h.transform_keys(&:last) } end
如果使用Groupdate库可以进一步简化代码,同时自动处理时区、时间连续性的问题:
group_period = :day result = LineItem.joins(:order, :product) .where(orders: { order_date: Date.parse("Jan 1 2020")..Date.parse("Feb 1 2020") }) .group("products.model_number") .group_by_period(group_period, "orders.order_date", format: "%a %b %d, %Y") .sum(:order_quantity) .group_by { |(model, _), _| model } .map do |model, pairs| { name: model, data: pairs.to_h.transform_keys(&:last) } end
内容的提问来源于stack exchange,提问作者Tyler P
相关产品推荐
相关产品推荐

