Ruby on rails 在ruby中按年龄范围对用户进行分组

Ruby on rails 在ruby中按年龄范围对用户进行分组,ruby-on-rails,ruby,activerecord,group-by,range,Ruby On Rails,Ruby,Activerecord,Group By,Range,我试图按年龄范围列出用户数量: Range : #Users 10-14 : 16 15-21 : 120 22-29 : 312 30-40 : 12131 41-70 : 612 71-120 : 20 我在考虑创建一个静态哈希数组: AGE_RANGES = [ {label:"10 - 14", min:10, max:14}, {label:"15 - 21", min:15, max:21}, {label:"22 - 29", min:22, max:29

我试图按年龄范围列出用户数量:

Range  : #Users
10-14  : 16
15-21  : 120
22-29  : 312
30-40  : 12131
41-70  : 612
71-120 : 20
我在考虑创建一个静态哈希数组:

AGE_RANGES = [
  {label:"10 - 14", min:10, max:14},
  {label:"15 - 21", min:15, max:21},
  {label:"22 - 29", min:22, max:29},
  {label:"30 - 40", min:30, max:40},
  {label:"41 - 70", min:41, max:70},
  {label:"71 - 120", min:71, max:120}
]
然后将其用于我的搜索过滤器以及我的查询。但是,我想不出一种方法来获得最大的性能

我的模型中的方法仅按年龄分组:

def self.group_by_ageRange(minAge, maxAge)

  query = User.group("users.age")
              .where("users.age BETWEEN minAge and maxAge ")
              .select("users.age,
                        count(*) as number_of_users")

end

有什么建议吗?

您想构建如下所示的SQL:

select count(*),
       case
           when age between 10 and 14 then '10 - 14'
           when age between 15 and 21 then '15 - 21'
           -- ...
       end as age_range
from users
where age between 10 and 120
group by age_range
{
  0 => 0,
  1 => 1,
  3 => 5,
  25 => 3,
  99 => 3
}
用ActiveRecord的术语来说,这将是:

# First build the big ugly CASE, we can also figure out the
# overall max and min ages along the way.
min   = nil
max   = nil
cases = AGE_RANGES.map do |r|
    min = [r[:min], min || r[:min]].min
    max = [r[:max], max || r[:max]].max
    "when age between #{r[:min]} and #{r[:max]} then '#{r[:min]} - #{r[:max]}'"
end

# Then away we go...
age_ranges = Users.select("count(*) as n, case #{cases.join(' ')} end as age_range")
                  .where(:age => min .. max)
                  .group('age_range')
                  .all
这将为您留下
age\u范围内的对象数组
,这些对象将具有
n
age\u范围内的方法。如果您想从中得到一个哈希值,那么:

age_ranges = Hash[age_ranges.map { |r| [r.age_range, r.n] }]

当然,这不包括没有人的范围;我将把它作为练习留给读者。

我发现公认的答案有点晦涩难懂。快速但难以理解和书写。今天,我想出了一个更慢但更简单的解决方案。由于我们将年龄分为不同的范围,我们可以假设我们不会有

这意味着,如果对分组和计数的结果集使用ruby过滤器,则迭代的项目不会超过125个。这将比基于sql范围的组/计数慢,但对于我来说,这已经足够快了,同时在大多数繁重的工作中仍然依赖于DB。在一个少于125项的散列上迭代似乎没什么大不了的。特别是当键值对仅为int时,如下所示:

select count(*),
       case
           when age between 10 and 14 then '10 - 14'
           when age between 15 and 21 then '15 - 21'
           -- ...
       end as age_range
from users
where age between 10 and 120
group by age_range
{
  0 => 0,
  1 => 1,
  3 => 5,
  25 => 3,
  99 => 3
}
以下是psudo代码:

users = User
  .where(age: (min..max))
  .group(:age)
  .count(:age)
group = Hash.new(0)
users.each{|age, count|
      case
      when age <= 10
        group['under 10'] += count
      when age <= 25
        group['11-25'] += count
      when age <= 40
        group['26-40'] += count
      else
        group['41+'] += count
      end
}
users=User
.其中(年龄:(最小..最大))
.组别(:年龄)
.计数(:年龄)
组=哈希。新建(0)
用户。每个{年龄,计数|
案例

当这个年龄解决了我的问题时,谢谢。我现在似乎在控制器中有一些rails错误…如果我添加
logger.debug(“items:{@ageRange\u items.inspect}”)
一切正常…如果没有,它只是设置
cases.join(“”)
作为
age\u range
,给出了一个自然错误,说
列age\u range不存在
@itsalltime:如果不看最后的代码,就很难诊断。它是这样的……它比我发布的问题要复杂一点,因为它有链式连接,但不应该有这种行为
query=User.joins(shops:{receives:{tag::user})。选择(“case#{cases.join(“”)}结束为年龄范围,计数(*)为年龄范围内的用户数,总和(总计)为总数”)。其中(“users.id=:user_id,user_id:user)。其中(“users_tags.age”=>min..max.group(“年龄范围”)
听起来像
@ageRange\u items
进入您的视图时可能仍然是一个关系,而不是一个数组。我没有看到
.all
。要在任何地方使用
,那么您可能不会得到您认为是的
任何?
如果@ageRange\u items.present?
可能会更惯用。ActiveRecord不会接触数据库,除非您通过调用
.all
.to_a
。each
,…当您说
x=M.where(…)
,您会得到一个关系对象,这样您就可以链接方法来构建查询:
M.where(…).where(…).order(…).where(…)
。当您
x.all
时,您与数据库对话并获得一些结果。我猜
x.inspect
会获得结果并缓存它们。您会注意到这是由ActiveRecord::Relation直接实现的,所以您需要
全部
或获得AR的
任何?