Rails多列分组结果格式化及补全零值组合需求
为车辆API实现多分组嵌套结构并补全零值组合
当前车辆API仅支持单列分组查询,例如:
Vehicle.group(:fuel_type).count
返回格式符合API需求:
{ "Petrol": 78, "Diesel": 22 }
但使用多列分组时,返回结果格式混乱,且缺失部分零值组合:
Vehicle.group(:fuel_type, :registration_status).count
返回的哈希键为字符串化的数组,且像Petrol + Exported这类无数据的组合直接缺失:
{ "['Diesel', 'Scrapped']": 5, "['Petrol', 'Registered']": 6, "['Petrol', 'Scrapped']": 30, "['Diesel', 'Registered']": 1, "['Diesel', 'Deregistered']": 11, "['Petrol', 'Deregistered']": 42, "['Diesel', 'Exported']": 5 }
期望得到嵌套结构的结果,并补全所有可能的分组组合(包括计数为0的情况):
{ "Diesel": { "Scrapped": 5, "Registered": 1, "Deregistered": 11, "Exported": 5 }, "Petrol": { "Scrapped": 30, "Registered": 6, "Deregistered": 42, "Exported": 0 } }
理想情况下支持任意n层嵌套分组,每层都显示所有可能的组合,计数为0的组合也需包含。
解决方案
核心思路
- 先获取每个分组字段的所有可能取值(枚举值或数据库中已存在的唯一值)
- 将原始多列分组的扁平哈希转换为嵌套结构
- 遍历所有可能的分组组合,补全缺失项并设置计数为0
基础实现(两列分组)
1. 获取分组字段的所有可能值
如果字段是Rails枚举类型,直接调用枚举的values方法:
# 假设Vehicle模型定义了对应enum # enum fuel_type: { petrol: 'Petrol', diesel: 'Diesel' } # enum registration_status: { registered: 'Registered', scrapped: 'Scrapped', deregistered: 'Deregistered', exported: 'Exported' } fuel_types = Vehicle.fuel_types.values statuses = Vehicle.registration_status.values
如果是普通字段,从数据库提取唯一值:
fuel_types = Vehicle.pluck(:fuel_type).uniq.compact statuses = Vehicle.pluck(:registration_status).uniq.compact
2. 转换原始结果为嵌套哈希
raw_counts = Vehicle.group(:fuel_type, :registration_status).count nested_counts = raw_counts.each_with_object({}) do |(keys, count), result| fuel_type, status = keys result[fuel_type] ||= {} result[fuel_type][status] = count end
3. 补全零值组合
fuel_types.each do |fuel| statuses.each do |status| nested_counts[fuel] ||= {} nested_counts[fuel][status] ||= 0 end end
通用实现(支持n层分组)
编写递归方法处理任意层数的分组需求:
def build_nested_group_counts(model, group_fields) # 收集每个分组字段的所有可能值 enum_values = group_fields.map do |field| # 优先处理Rails enum字段 enum_method = field.to_s.pluralize if model.respond_to?(enum_method) && model.send(enum_method).is_a?(Hash) model.send(enum_method).values else # 普通字段从数据库取唯一值 model.pluck(field).uniq.compact end end # 获取原始分组计数 raw_counts = model.group(*group_fields).count # 递归构建全组合嵌套哈希 fill_nested = lambda do |layers, data, path| if layers.size == 1 # 底层:补全所有可能值,缺失则设为0 layers.first.each_with_object({}) do |val, hash| hash[val] = data.fetch(path + [val], 0) end else # 递归处理上层节点 layers.first.each_with_object({}) do |val, hash| hash[val] = fill_nested.call(layers[1..-1], data, path + [val]) end end end fill_nested.call(enum_values, raw_counts, []) end
使用示例
# 两列分组 build_nested_group_counts(Vehicle, [:fuel_type, :registration_status]) # 三列分组(添加body_type字段) build_nested_group_counts(Vehicle, [:fuel_type, :registration_status, :body_type])
这个方法会自动生成嵌套结构的哈希,补全所有可能的分组组合,无数据的组合计数设为0,完全适配API响应格式。
内容的提问来源于stack exchange,提问作者TobiasKnudsen
相关产品推荐
相关产品推荐

