You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 6使用reduce生成嵌套哈希计算各区域chart_axis_values平均值

核心问题排查

你现有代码的核心错误有3个:

  1. merge 方法是非原地修改方法,你写的 values[area.name].merge({axis.name=>{count: 0, total: 0.0}}) 不会修改原哈希,导致新增维度时初始化失效,后续访问对应属性会报空指针错误
  2. 最终计算平均值的遍历逻辑层级错误,当前存储结构是区域→维度→统计值,你原来的单层遍历无法匹配这个嵌套结构
  3. 未预加载关联数据存在严重的N+1查询问题,数据量稍大就会导致接口性能极差

修复后完整代码

控制器代码(增加预加载优化)

def index
  # 预加载关联数据彻底解决N+1查询问题
  collection = Chart.includes(employee: :areas, :chart_axis_values).all
  render json: collection, meta: {averages: collection.averages }
end

模型averages方法代码

## /models/chart.rb
class Chart < ApplicationRecord
  belongs_to :employee
  has_many :chart_axis_values

  def self.averages
    # 用哈希默认Proc自动处理嵌套结构初始化,不需要手动判断空值
    area_stats = Hash.new { |h, area| h[area] = Hash.new { |dh, dim| dh[dim] = {count: 0, total: 0.0} } }

    all.each do |chart|
      chart.chart_axis_values.each do |axis|
        # 跳过空值数据
        next if axis.value.nil?
        chart.employee.areas.each do |area|
          # 直接累加计数和总和,初始化逻辑已经由哈希默认Proc处理
          area_stats[area.name][axis.name][:count] += 1
          area_stats[area.name][axis.name][:total] += axis.value
        end
      end
    end

    # 两层遍历计算每个区域下各维度的平均值
    area_stats.each_with_object({}) do |(area_name, dim_stats), result|
      result[area_name] = dim_stats.each_with_object({}) do |(dim_name, stats), dim_result|
        dim_result[dim_name] = stats[:total] / stats[:count]
      end
    end
  end
end

返回结果格式示例

{
  "averages": {
    "华东区": {
      "motivation": 4.2,
      "satisfaction": 3.8
    },
    "华南区": {
      "motivation": 4.5,
      "satisfaction": 4.1
    }
  }
}

可选兼容方案

如果你需要同时支持全量平均值和按区域拆分平均值两种模式,可以给方法加可选参数:

def self.averages(group_by_area: false)
  # 不传参数默认返回原来的全量维度平均值
  unless group_by_area
    values = all.reduce({}) do |values, chart|
      chart.chart_axis_values.each do |axis|
        next if axis.value.nil?
        values[axis.slug] ||= {count: 0, total: 0.0}
        values[axis.slug][:count] += 1
        values[axis.slug][:total] += axis.value
      end
      values
    end
    return values.each_with_object({}) { |(axis, stats), res| res[axis] = stats[:total]/stats[:count] }
  end

  # 传group_by_area: true时返回按区域拆分的平均值
  area_stats = Hash.new { |h, area| h[area] = Hash.new { |dh, dim| dh[dim] = {count: 0, total: 0.0} } }
  all.each do |chart|
    chart.chart_axis_values.each do |axis|
      next if axis.value.nil?
      chart.employee.areas.each do |area|
        area_stats[area.name][axis.name][:count] += 1
        area_stats[area.name][axis.name][:total] += axis.value
      end
    end
  end
  area_stats.each_with_object({}) do |(area_name, dim_stats), result|
    result[area_name] = dim_stats.each_with_object({}) do |(dim_name, stats), dim_result|
      dim_result[dim_name] = stats[:total] / stats[:count]
    end
  end
end

调用时按需求传参即可:collection.averages(group_by_area: true)


内容的提问来源于stack exchange,提问作者GuidoMedina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:45:00