Home > Net >  Group users by age range in rails
Group users by age range in rails

Time:04-12

Based on 'PESEL" number I have to group user by their age. I created something like this and it is working, but... To be honest, it look bad for me.

HELPER:

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

CONTROLLER:

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

A small explanation and example. I am interested in first 6 numbers that correspond sequentially: Year of birth, month, day So if my PESEL == '980129(...)', then I was born twenty-ninth of January 1998 If someone was born in year 2000, then we add 20 to pesel-month number(for example '002129(...)' it is twenty-ninth of January 2000. If someone was born 2100, then we add 40 to pesel-month number. I have explained what the pesel number is all about, now what I want to do with it. I need to group users by their age range. Function from above returns has like this:

{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",(...)}

Unfortunately this is not very efficient, because for each user (4000 max) I have to execute the functions from scratch. Is there any way to increase efficiency of this? I thought about storing this hash as const and changing it once a year, but I don't really know how to do that or if it is possible.

EDIT: Forgot to mention: I need to compare user age with hash, so I can extract age range

CodePudding user response:

Instead of doing the complicated calculating of birth date from PESEL every time you want to view the page, do it once and store it in the database. Having a birth date column on the user makes a lot of sense.

Then when you want to group them, you can even do it via the database. If you still need to do it in ruby, then getting the birth year is as easy as user.birth_date.year

In order to then group users into ranges of 5 years according to age, add an age_range method to the model and group by that.

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

Where age_range can be for example

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

Format that however you like

CodePudding user response:

I guess you could at least build the cache once then use it in your loop. The following code is not pretty, it's just to illustrate what I mean:

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
  • Related