按 rails 中的年龄范围对用户进行分组

Group users by age range in rails

根据“PESEL”数字,我必须按年龄对用户进行分组。我创建了类似这样的东西并且它正在运行,但是......老实说,它对我来说看起来很糟糕。

帮助者:

def years(pesel)
    years = (0..99).to_a
    birth_year = []
    case pesel[2..3].to_i
    when 0..19
      20.times do |index|
        first_number = index % 2 == 0 ? (5 * index) : ((5 * index))
        second_number = index % 2 == 0 ? (5 * index + 4) : ((5 * index) + 4)
        first_year = Date.today.year - second_number.to_s.rjust(4,'1900').to_i
        second_year = Date.today.year - first_number.to_s.rjust(4,'1900').to_i
        birth_year += ["#{first_year}-#{second_year}"]
      end
      multiplied_birth_years = ([birth_year] * 5).inject(&:zip).flatten
      hash = Hash[years.zip multiplied_birth_years]
      hash.fetch(pesel[0..1].to_i)
    when 20..39
      20.times do |index|
        first_number = index % 2 == 0 ? (5 * index) : ((5 * index))
        second_number = index % 2 == 0 ? (5 * index + 4) : ((5 * index) + 4)
        first_year = Date.today.year - second_number.to_s.rjust(4,'2000').to_i
        second_year = Date.today.year - first_number.to_s.rjust(4,'2000').to_i
        birth_year += ["#{first_year}-#{second_year}"]
      end
      multiplied_birth_years = ([birth_year] * 5).inject(&:zip).flatten
      hash = Hash[years.zip multiplied_birth_years]
      hash.fetch(pesel[0..1].to_i)
    when 40..59
      20.times do |index|
        first_number = index % 2 == 0 ? (5 * index) : ((5 * index))
        second_number = index % 2 == 0 ? (5 * index + 4) : ((5 * index) + 4)
        first_year = Date.today.year - second_number.to_s.rjust(4,'2100').to_i
        second_year = Date.today.year - first_number.to_s.rjust(4,'2100').to_i
        birth_year += ["#{first_year}-#{second_year}"]
      end
      multiplied_birth_years = ([birth_year] * 5).inject(&:zip).flatten
      hash = Hash[years.zip multiplied_birth_years]
      hash.fetch(pesel[0..1].to_i)
    end
  end

控制器:

def grouped_by_age
    @yearsbook = @study_participations.includes(user: :profile).group_by do |study_participation|
      years(study_participation.user.profile.pesel)
    end
  end

一个小的解释和例子。我对顺序对应的前 6 个数字感兴趣:出生年份、月份、日期 所以如果我的 PESEL == '980129(...)',那么我出生于 1998 年 1 月 29 日 如果某人出生于 2000 年,那么我们将 20 添加到 pesel-month(例如 '002129(...)' 它是 2000 年 1 月 29 日。如果某人出生于 2100 年,那么我们将 40 添加到 pesel-月号。 我已经解释了 pesel 数是什么,现在我想用它做什么。 我需要按年龄范围对用户进行分组。上面的函数 returns 是这样的:

{0=>"118-122",
 1=>"118-122",
 2=>"118-122",
 3=>"118-122",
 4=>"118-122",
 5=>"113-117",
 6=>"113-117",
 7=>"113-117",
 8=>"113-117",
 9=>"113-117",
 10=>"108-112",
 11=>"108-112",
 12=>"108-112",
 13=>"108-112",
 14=>"108-112",
 15=>"103-107",
 16=>"103-107",
 17=>"103-107",
 18=>"103-107",
 19=>"103-107",(...)}

不幸的是,这不是很有效,因为对于每个用户(最多 4000 个)我必须从头开始执行这些功能。有什么办法可以提高效率吗?我考虑过将此哈希存储为 const 并每年更改一次,但我真的不知道该怎么做或是否可行。

编辑: 忘了说:我需要比较用户年龄和hash,所以我可以提取年龄范围

编辑2: 基于@yoones 的回答,我创建了这样的东西:

帮助者:

def years_cache
    years = []
    201.times do |index|
      years += [Date.today.year - (1900 + index)]
    end
    birth_year = []
    60.times do |index|
      year = if index < 20
               '1900'
             elsif index < 40
               '2000'
             else
               '2100'
             end
      first_number = 5 * (index % 20)
      second_number = (5 * (index % 20)) + 4
      first_year = Date.today.year - second_number.to_s.rjust(4, year).to_i
      second_year = Date.today.year - first_number.to_s.rjust(4, year).to_i
      birth_year += ["#{first_year}-#{second_year}"]
    end
    multiplied_birth_years = ([birth_year] * 5).inject(&:zip).flatten
    @hash = (years.zip multiplied_birth_years).to_h
  end

  def years(cache, pesel)
    day = pesel[4..5]
    case pesel[2..3].to_i
    when 0..19
      month = pesel[2..3]
      year = pesel[0..1].prepend('19')
    when 20..39
      month = (pesel[2..3].to_i - 20).to_s
      year = pesel[0..1].prepend('20')
    when 40..59
      month = (pesel[2..3].to_i - 40).to_s
      year = pesel[0..1].prepend('21')
    end
    birth_date = Time.strptime("#{day}/#{month}/#{year}", '%d/%m/%Y')
    age = ((Time.zone.now - birth_date) / 1.year.seconds).floor
    cache.fetch(age)
  end

控制器:

def grouped_by_age
    cache = years_cache()
    @yearsbook = @study_participations.includes(user: :profile).group_by do |study_participation|
      years(cache, study_participation.user.profile.pesel)
    end
  end

与其每次要查看页面时都从 PESEL 进行复杂的出生日期计算,不如执行一次并将其存储在数据库中。在用户上设置出生日期列很有意义。

然后当你想对它们进行分组时,你甚至可以通过数据库来完成。如果你还需要在ruby中完成,那么获取出生年份就像user.birth_date.year

一样简单

为了根据年龄将用户分组到 5 岁的范围内,向模型添加一个 age_range 方法并按该方法分组。

@study_participations.includes(user: :profile).group_by do |study_participation|
  study_participation.user.age_range
end

其中age_range可以是例如

def age_range
  (Date.today.year - birth_date.year) / 5) * 5
end

随心所欲地格式化

我想您至少可以构建一次缓存,然后在循环中使用它。下面的代码并不漂亮,只是为了说明我的意思:

def build_year_cache(index, rjust_str)
  first_number = 5 * index
  second_number = index % 2 == 0 ? (5 * index + 4) : ((5 * index) + 4)
  first_year = Date.today.year - second_number.to_s.rjust(4, rjust_str).to_i
  second_year = Date.today.year - first_number.to_s.rjust(4, rjust_str).to_i
  "#{first_year}-#{second_year}"
end

def build_years_cache
  cache = {}
  years = (0..99).to_a

  [
    [0..19, '1900'],
    [20..39, '2000'],
    [40..59, '2100']
  ].each do |range, rjust_str|
    birth_year = []
    20.times do |index|
      birth_year.append(build_year_cache(index, rjust_str))
    end
    multiplied_birth_years = ([birth_year] * 5).inject(&:zip).flatten
    cache[range] = Hash[years.zip multiplied_birth_years]
  end

  cache
end

def years(pesel, cache)
  year = pesel[0..1].to_i
  month = pesel[2..3].to_i
  range = cache.keys.find { |k, v| k.include?(month) }
  cache[range].fetch(year)
end


def grouped_by_age
  cache = build_years_cache
  @yearsbook = @study_participations.includes(user: :profile).group_by do |study_participation|
    years(study_participation.user.profile.pesel, cache)
  end
end